summaryrefslogtreecommitdiff
path: root/Plugins/jingle/libjingle/talk/session
diff options
context:
space:
mode:
Diffstat (limited to 'Plugins/jingle/libjingle/talk/session')
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am7
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in446
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc1313
-rw-r--r--Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h250
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/Makefile.am27
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/Makefile.in485
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc120
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h74
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/call.cc336
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/call.h116
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc219
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/channelmanager.h80
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/codec.h47
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc239
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h93
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc207
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h88
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/mediachannel.h78
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/mediaengine.h70
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc278
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h132
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc330
-rw-r--r--Plugins/jingle/libjingle/talk/session/phone/voicechannel.h137
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am7
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in452
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc554
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h125
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc316
-rw-r--r--Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h135
29 files changed, 6761 insertions, 0 deletions
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am
new file mode 100644
index 0000000..a6eebf6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.am
@@ -0,0 +1,7 @@
+libcricketsessionfileshare_la_SOURCES = fileshare.cc
+
+noinst_HEADERS = fileshare.h
+
+AM_CPPFLAGS := -DPOSIX
+noinst_LTLIBRARIES = libcricketsessionfileshare.la
+
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in
new file mode 100644
index 0000000..3c9cd69
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/Makefile.in
@@ -0,0 +1,446 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/fileshare
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessionfileshare_la_LIBADD =
+am_libcricketsessionfileshare_la_OBJECTS = fileshare.lo
+libcricketsessionfileshare_la_OBJECTS = \
+ $(am_libcricketsessionfileshare_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessionfileshare_la_SOURCES)
+DIST_SOURCES = $(libcricketsessionfileshare_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+libcricketsessionfileshare_la_SOURCES = fileshare.cc
+noinst_HEADERS = fileshare.h
+AM_CPPFLAGS := -DPOSIX
+noinst_LTLIBRARIES = libcricketsessionfileshare.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/fileshare/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/fileshare/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessionfileshare.la: $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessionfileshare_la_LDFLAGS) $(libcricketsessionfileshare_la_OBJECTS) $(libcricketsessionfileshare_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileshare.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc
new file mode 100644
index 0000000..863c352
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.cc
@@ -0,0 +1,1313 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/session/fileshare/fileshare.h"
+
+#include "talk/base/httpcommon-inl.h"
+
+#include "talk/base/fileutils.h"
+#include "talk/base/streamutils.h"
+#include "talk/base/event.h"
+#include "talk/base/helpers.h"
+#include "talk/base/httpclient.h"
+#include "talk/base/httpserver.h"
+#include "talk/base/pathutils.h"
+#include "talk/base/socketstream.h"
+#include "talk/base/stringdigest.h"
+#include "talk/base/stringencode.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/tarstream.h"
+#include "talk/base/thread.h"
+#include "talk/session/tunnel/pseudotcpchannel.h"
+#include "talk/session/tunnel/tunnelsessionclient.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// <description xmlns="http://www.google.com/session/share">
+// <manifest>
+// <file size='341'>
+// <name>foo.txt</name>
+// </file>
+// <file size='51321'>
+// <name>foo.jpg</name>
+// <image width='480' height='320'/>
+// </file>
+// <folder>
+// <name>stuff</name>
+// </folder>
+// </manifest>
+// <protocol>
+// <http>
+// <url name='source-path'>/temporary/23A53F01/</url>
+// <url name='preview-path'>/temporary/90266EA1/</url>
+// </http>
+// <raw/>
+// </protocol>
+// </description>
+// <p:transport xmns:p="p2p"/>
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+// Constants and private functions
+///////////////////////////////////////////////////////////////////////////////
+
+const std::string NS_GOOGLE_SHARE("http://www.google.com/session/share");
+
+namespace {
+
+const buzz::QName QN_SHARE_DESCRIPTION(true, NS_GOOGLE_SHARE, "description");
+const buzz::QName QN_SHARE_MANIFEST(true, NS_GOOGLE_SHARE, "manifest");
+const buzz::QName QN_SHARE_FOLDER(true, NS_GOOGLE_SHARE, "folder");
+const buzz::QName QN_SHARE_FILE(true, NS_GOOGLE_SHARE, "file");
+const buzz::QName QN_SHARE_NAME(true, NS_GOOGLE_SHARE, "name");
+const buzz::QName QN_SHARE_IMAGE(true, NS_GOOGLE_SHARE, "image");
+const buzz::QName QN_SHARE_PROTOCOL(true, NS_GOOGLE_SHARE, "protocol");
+const buzz::QName QN_SHARE_HTTP(true, NS_GOOGLE_SHARE, "http");
+const buzz::QName QN_SHARE_URL(true, NS_GOOGLE_SHARE, "url");
+const buzz::QName QN_SHARE_CHANNEL(true, NS_GOOGLE_SHARE, "channel");
+const buzz::QName QN_SHARE_COMPLETE(true, NS_GOOGLE_SHARE, "complete");
+
+const buzz::QName QN_SIZE(true, buzz::STR_EMPTY, "size");
+const buzz::QName QN_WIDTH(true, buzz::STR_EMPTY, "width");
+const buzz::QName QN_HEIGHT(true, buzz::STR_EMPTY, "height");
+
+const std::string kHttpSourcePath("source-path");
+const std::string kHttpPreviewPath("preview-path");
+
+const size_t kMinImageSize = 16U;
+const size_t kMaxImageSize = 0x8000U; // (32k)
+const size_t kMaxPreviewSize = 1024;
+// Wait 10 seconds to see if any new proxies get established
+const uint32 kProxyWait = 10000;
+
+const int MSG_RETRY = 1;
+const uint32 kFileTransferEnableRetryMs = 1000 * 60 * 4; // 4 minutes
+
+const std::string MIME_OCTET_STREAM("application/octet-stream");
+
+enum {
+ MSG_PROXY_WAIT,
+};
+
+bool AllowedImageDimensions(size_t width, size_t height) {
+ return (width >= kMinImageSize) && (width <= kMaxImageSize)
+ && (height >= kMinImageSize) && (height <= kMaxImageSize);
+}
+
+} // anon namespace
+
+namespace cricket {
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareManifest
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileShareManifest::AddFile(const std::string& name, size_t size) {
+ Item i = { T_FILE, name, size };
+ items_.push_back(i);
+}
+
+void
+FileShareManifest::AddImage(const std::string& name, size_t size,
+ size_t width, size_t height) {
+ Item i = { T_IMAGE, name, size, width, height };
+ items_.push_back(i);
+}
+
+void
+FileShareManifest::AddFolder(const std::string& name, size_t size) {
+ Item i = { T_FOLDER, name, size };
+ items_.push_back(i);
+}
+
+size_t
+FileShareManifest::GetItemCount(Type t) const {
+ size_t count = 0;
+ for (size_t i=0; i<items_.size(); ++i) {
+ if (items_[i].type == t)
+ ++count;
+ }
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareSession
+///////////////////////////////////////////////////////////////////////////////
+
+FileShareSession::FileShareSession(cricket::Session* session, const std::string &user_agent)
+ : session_(session), state_(FS_NONE),
+ is_closed_(false),
+ is_sender_(false), manifest_(NULL), pool_(this), http_client_(NULL),
+ http_server_(NULL),
+ transfer_connection_id_(talk_base::HTTP_INVALID_CONNECTION_ID),
+ counter_(NULL), item_transferring_(0), bytes_transferred_(0),
+ local_cancel_(false), local_listener_(NULL), remote_listener_(NULL),
+ next_channel_id_(1), user_agent_(user_agent) {
+ session_->SignalState.connect(this, &FileShareSession::OnSessionState);
+ session_->SignalInfoMessage.connect(this,
+ &FileShareSession::OnSessionInfoMessage);
+ session_->SignalChannelGone.connect(this,
+ &FileShareSession::OnSessionChannelGone);
+}
+
+FileShareSession::~FileShareSession() {
+ ASSERT(FS_NONE != state_);
+ // If we haven't closed, do cleanup now.
+ if (!IsClosed()) {
+ if (!IsComplete()) {
+ state_ = FS_FAILURE;
+ }
+ DoClose(true);
+ }
+ if (session_) {
+ // Make sure we don't get future state changes on this session.
+ session_->SignalState.disconnect(this);
+ session_->SignalInfoMessage.disconnect(this);
+ session_ = NULL;
+ }
+
+ for (TransactionList::const_iterator trans_it = transactions_.begin();
+ trans_it != transactions_.end(); ++trans_it) {
+ (*trans_it)->response()->set_error(talk_base::HC_NOT_FOUND);
+ http_server_->Respond(*trans_it);
+ }
+
+ delete http_client_;
+ delete http_server_;
+ delete manifest_;
+ delete local_listener_;
+ delete remote_listener_;
+}
+
+bool
+FileShareSession::IsComplete() const {
+ return (state_ >= FS_COMPLETE);
+}
+
+bool
+FileShareSession::IsClosed() const {
+ return is_closed_;
+}
+
+FileShareState
+FileShareSession::state() const {
+ return state_;
+}
+
+bool
+FileShareSession::is_sender() const {
+ ASSERT(FS_NONE != state_);
+ return is_sender_;
+}
+
+const buzz::Jid&
+FileShareSession::jid() const {
+ ASSERT(FS_NONE != state_);
+ return jid_;
+}
+
+const FileShareManifest*
+FileShareSession::manifest() const {
+ ASSERT(FS_NONE != state_);
+ return manifest_;
+}
+
+const std::string&
+FileShareSession::local_folder() const {
+ ASSERT(!local_folder_.empty());
+ return local_folder_;
+}
+
+void
+FileShareSession::Share(const buzz::Jid& jid, FileShareManifest* manifest) {
+ ASSERT(FS_NONE == state_);
+ ASSERT(NULL != session_);
+
+ http_server_ = new talk_base::HttpServer;
+ http_server_->SignalHttpRequest.connect(this,
+ &FileShareSession::OnHttpRequest);
+ http_server_->SignalHttpRequestComplete.connect(this,
+ &FileShareSession::OnHttpRequestComplete);
+ http_server_->SignalConnectionClosed.connect(this,
+ &FileShareSession::OnHttpConnectionClosed);
+
+ FileShareDescription* desc = new FileShareDescription;
+ desc->supports_http = true;
+ desc->manifest = *manifest;
+ GenerateTemporaryPrefix(&desc->source_path);
+ GenerateTemporaryPrefix(&desc->preview_path);
+ session_->Initiate(jid.Str(), NULL, desc);
+
+ delete manifest;
+}
+
+void
+FileShareSession::Accept() {
+ ASSERT(FS_OFFER == state_);
+ ASSERT(NULL != session_);
+ ASSERT(NULL != manifest_);
+
+ ASSERT(!http_client_);
+ ASSERT(item_transferring_ == 0);
+ http_client_ = new talk_base::HttpClient(user_agent_,
+ &pool_);
+ http_client_->SignalHttpClientComplete.connect(this,
+ &FileShareSession::OnHttpClientComplete);
+ http_client_->SignalHttpClientClosed.connect(this,
+ &FileShareSession::OnHttpClientClosed);
+
+ // The receiver now has a need for the http_server_, when previewing already
+ // downloaded content.
+ http_server_ = new talk_base::HttpServer;
+ http_server_->SignalHttpRequest.connect(this,
+ &FileShareSession::OnHttpRequest);
+ http_server_->SignalHttpRequestComplete.connect(this,
+ &FileShareSession::OnHttpRequestComplete);
+ http_server_->SignalConnectionClosed.connect(this,
+ &FileShareSession::OnHttpConnectionClosed);
+
+ FileShareDescription* desc = new FileShareDescription;
+ desc->supports_http = description()->supports_http;
+ session_->Accept(desc);
+
+ SetState(FS_TRANSFER, false);
+ NextDownload();
+}
+
+void
+FileShareSession::Decline() {
+ ASSERT(FS_OFFER == state_);
+ ASSERT(NULL != session_);
+ local_cancel_ = true;
+ session_->Reject();
+}
+
+void
+FileShareSession::Cancel() {
+ ASSERT(!IsComplete());
+ ASSERT(NULL != session_);
+ local_cancel_ = true;
+ session_->Terminate();
+}
+
+bool
+FileShareSession::GetItemUrl(size_t index, std::string* url) {
+ return GetItemBaseUrl(index, false, url);
+}
+
+bool FileShareSession::GetImagePreviewUrl(size_t index, size_t width,
+ size_t height, std::string* url) {
+ if (!GetItemBaseUrl(index, true, url))
+ return false;
+
+ if (FileShareManifest::T_IMAGE != manifest_->item(index).type) {
+ ASSERT(false);
+ return false;
+ }
+
+ char query[256];
+ talk_base::sprintfn(query, ARRAY_SIZE(query), "?width=%u&height=%u",
+ width, height);
+ url->append(query);
+ return true;
+}
+
+void FileShareSession::ResampleComplete(talk_base::StreamInterface *i, talk_base::HttpTransaction *trans, bool success) {
+ bool found = false;
+ for (TransactionList::const_iterator trans_it = transactions_.begin();
+ trans_it != transactions_.end(); ++trans_it) {
+ if (*trans_it == trans) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ transactions_.remove(trans);
+
+ if (success) {
+ trans->response()->set_success(MIME_OCTET_STREAM, i);
+ http_server_->Respond(trans);
+
+ }
+ trans->response()->set_error(talk_base::HC_NOT_FOUND);
+ http_server_->Respond(trans);
+}
+
+bool FileShareSession::GetProgress(size_t& bytes) const {
+ bool known = true;
+ bytes = bytes_transferred_;
+ if (counter_) {
+ size_t current_size = manifest_->item(item_transferring_).size;
+ size_t current_pos = counter_->GetByteCount();
+ if (current_size == FileShareManifest::SIZE_UNKNOWN) {
+ known = false;
+ } else if (current_pos > current_size) {
+ // Don't allow the size of a 'known' item to be reported as larger than
+ // it claimed to be.
+ ASSERT(false);
+ current_pos = current_size;
+ }
+ bytes += current_pos;
+ }
+ return known;
+}
+
+bool FileShareSession::GetTotalSize(size_t& bytes) const {
+ bool known = true;
+ bytes = 0;
+ for (size_t i=0; i<manifest_->size(); ++i) {
+ if (manifest_->item(i).size == FileShareManifest::SIZE_UNKNOWN) {
+ // We make files of unknown length worth a single byte.
+ known = false;
+ bytes += 1;
+ } else {
+ bytes += manifest_->item(i).size;
+ }
+ }
+ return known;
+}
+
+bool FileShareSession::GetCurrentItemName(std::string* name) {
+ if (FS_TRANSFER != state_) {
+ name->clear();
+ return false;
+ }
+ ASSERT(item_transferring_ < manifest_->size());
+ if (transfer_name_.empty()) {
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ *name = item.name;
+ } else {
+ *name = transfer_name_;
+ }
+ return !name->empty();
+}
+
+// StreamPool Implementation
+
+talk_base::StreamInterface* FileShareSession::RequestConnectedStream(
+ const talk_base::SocketAddress& remote, int* err) {
+ ASSERT(remote.IPAsString() == jid_.Str());
+ ASSERT(!IsClosed());
+ ASSERT(NULL != session_);
+ if (!session_) {
+ if (err)
+ *err = -1;
+ return NULL;
+ }
+
+ char channel_name[64];
+ talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name),
+ "private-%u", next_channel_id_++);
+ if (err)
+ *err = 0;
+ return CreateChannel(channel_name);
+}
+
+void FileShareSession::ReturnConnectedStream(
+ talk_base::StreamInterface* stream) {
+ talk_base::Thread::Current()->Dispose(stream);
+}
+
+// MessageHandler Implementation
+
+void FileShareSession::OnMessage(talk_base::Message* msg) {
+ if (MSG_PROXY_WAIT == msg->message_id) {
+ LOG_F(LS_INFO) << "MSG_PROXY_WAIT";
+ if (proxies_.empty() && IsComplete() && !IsClosed()) {
+ DoClose(true);
+ }
+ }
+}
+
+// Session Signals
+
+void FileShareSession::OnSessionState(cricket::Session* session,
+ cricket::Session::State state) {
+ // Once we are complete, state changes are meaningless.
+ if (!IsComplete()) {
+ switch (state) {
+ case cricket::Session::STATE_SENTINITIATE:
+ case cricket::Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case cricket::Session::STATE_SENTACCEPT:
+ case cricket::Session::STATE_RECEIVEDACCEPT:
+ case cricket::Session::STATE_INPROGRESS:
+ SetState(FS_TRANSFER, false);
+ break;
+ case cricket::Session::STATE_SENTREJECT:
+ case cricket::Session::STATE_SENTTERMINATE:
+ case cricket::Session::STATE_DEINIT:
+ if (local_cancel_) {
+ SetState(FS_LOCAL_CANCEL, false);
+ } else {
+ SetState(FS_REMOTE_CANCEL, false);
+ }
+ break;
+ case cricket::Session::STATE_RECEIVEDTERMINATE:
+ if (is_sender()) {
+ // If we are the sender, and the receiver downloaded the correct number
+ // of bytes, then we assume the transfer was successful. We've
+ // introduced support for explicit completion notification
+ // (QN_SHARE_COMPLETE), but it's not mandatory at this point, so we need
+ // this as a fallback.
+ size_t total_bytes;
+ GetTotalSize(total_bytes);
+ if (bytes_transferred_ >= total_bytes) {
+ SetState(FS_COMPLETE, false);
+ break;
+ }
+ }
+ // Fall through
+ case cricket::Session::STATE_RECEIVEDREJECT:
+ SetState(FS_REMOTE_CANCEL, false);
+ break;
+ case cricket::Session::STATE_INIT:
+ case cricket::Session::STATE_SENTMODIFY:
+ case cricket::Session::STATE_RECEIVEDMODIFY:
+ case cricket::Session::STATE_SENTREDIRECT:
+ default:
+ // These states should not occur.
+ ASSERT(false);
+ break;
+ }
+ }
+
+ if (state == cricket::Session::STATE_DEINIT) {
+ if (!IsClosed()) {
+ DoClose(false);
+ }
+ session_ = NULL;
+ }
+}
+
+void FileShareSession::OnSessionInfoMessage(cricket::Session* session,
+ const cricket::Session::XmlElements& els) {
+ if (IsClosed())
+ return;
+ ASSERT(NULL != session_);
+ for (size_t i=0; i<els.size(); ++i) {
+ if (is_sender() && (els[i]->Name() == QN_SHARE_CHANNEL)) {
+ if (els[i]->HasAttr(buzz::QN_NAME)) {
+ cricket::PseudoTcpChannel* channel =
+ new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ VERIFY(channel->Connect(els[i]->Attr(buzz::QN_NAME)));
+ talk_base::StreamInterface* stream = channel->GetStream();
+ http_server_->HandleConnection(stream);
+ }
+ } else if (is_sender() && (els[i]->Name() == QN_SHARE_COMPLETE)) {
+ // Normal file transfer has completed, but receiver may still be getting
+ // previews.
+ if (!IsComplete()) {
+ SetState(FS_COMPLETE, true);
+ }
+ } else {
+ LOG(LS_WARNING) << "Unknown FileShareSession info message: "
+ << els[i]->Name().Merged();
+ }
+ }
+}
+
+void FileShareSession::OnSessionChannelGone(cricket::Session* session,
+ const std::string& name) {
+ LOG_F(LS_WARNING) << "(" << name << ")";
+ ASSERT(session == session_);
+ if (cricket::TransportChannel* channel = session->GetChannel(name)) {
+ session->DestroyChannel(channel);
+ }
+}
+
+// HttpClient Signals
+
+void FileShareSession::OnHttpClientComplete(talk_base::HttpClient* http,
+ int err) {
+ LOG_F(LS_INFO) << "(" << err << ", " << http->response().scode << ")";
+ ASSERT(http == http_client_);
+ ASSERT(NULL != session_);
+
+ transfer_name_.clear();
+ counter_ = NULL; // counter_ is deleted by HttpClient
+ http->response().document.reset();
+ bool success = (err == 0) && (http->response().scode == talk_base::HC_OK);
+
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ talk_base::Pathname local_name;
+ local_name.SetFilename(item.name);
+ local_name.SetFolder(local_folder_);
+
+ if (local_name.pathname() != transfer_path_) {
+ const bool is_folder = (item.type == FileShareManifest::T_FOLDER);
+ if (success && !talk_base::CreateUniqueFile(local_name, false)) {
+ LOG(LS_ERROR) << "Couldn't rename downloaded file: "
+ << local_name.pathname();
+ success = false;
+ }
+
+ talk_base::Pathname temp_name(transfer_path_);
+ if (is_folder) {
+ // The folder we want is a subdirectory of the transfer_path_.
+ temp_name.AppendFolder(item.name);
+ }
+
+ if (!talk_base::Filesystem::MoveFile(temp_name.pathname(), local_name.pathname())) {
+ success = false;
+ LOG(LS_ERROR) << "Couldn't move downloaded file from '"
+ << temp_name.pathname() << "' to '"
+ << local_name.pathname();
+ }
+
+ if (success && is_folder) {
+ talk_base::Filesystem::DeleteFile(transfer_path_);
+ }
+ }
+
+ if (!success) {
+ if (!talk_base::Filesystem::DeleteFile(transfer_path_)) {
+ LOG(LS_ERROR) << "Couldn't delete downloaded file: " << transfer_path_;
+ }
+ if (!IsComplete()) {
+ SetState(FS_FAILURE, false);
+ }
+ return;
+ }
+
+ // We may have skipped over some items (if they are directories, or otherwise
+ // failed. resize ensures that we populate the skipped entries with empty
+ // strings.
+ stored_location_.resize(item_transferring_ + 1);
+ stored_location_[item_transferring_] = local_name.pathname();
+
+ // bytes_transferred_ represents the size of items which have completely
+ // transferred, and is added to the progress of the currently transferring
+ // items.
+ if (item.size == FileShareManifest::SIZE_UNKNOWN) {
+ bytes_transferred_ += 1;
+ } else {
+ bytes_transferred_ += item.size;
+ }
+ item_transferring_ += 1;
+ NextDownload();
+}
+
+void FileShareSession::OnHttpClientClosed(talk_base::HttpClient* http,
+ int err) {
+ LOG_F(LS_INFO) << "(" << err << ")";
+}
+
+// HttpServer Signals
+
+void FileShareSession::OnHttpRequest(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction) {
+ LOG_F(LS_INFO) << "(" << transaction->request()->path << ")";
+ ASSERT(server == http_server_);
+
+ std::string path, query;
+ size_t query_start = transaction->request()->path.find('?');
+ if (query_start != std::string::npos) {
+ path = transaction->request()->path.substr(0, query_start);
+ query = transaction->request()->path.substr(query_start + 1);
+ } else {
+ path = transaction->request()->path;
+ }
+
+ talk_base::Pathname remote_name(path);
+ bool preview = (preview_path_ == remote_name.folder());
+ bool original = (source_path_ == remote_name.folder());
+
+ std::string requested_file(remote_name.filename());
+ talk_base::transform(requested_file, requested_file.size(), requested_file,
+ talk_base::url_decode);
+
+ size_t item_index;
+ const FileShareManifest::Item* item = NULL;
+ if (preview || original) {
+ for (size_t i=0; i<manifest_->size(); ++i) {
+ LOG(LS_INFO) << "++++ " << manifest_->item(i).name + " " << requested_file;
+ if (manifest_->item(i).name == requested_file) {
+ item_index = i;
+ item = &manifest_->item(item_index);
+ break;
+ }
+ }
+ }
+
+ talk_base::StreamInterface* stream = NULL;
+ std::string mime_type(MIME_OCTET_STREAM);
+
+ if (!item) {
+ // Fall through
+ } else if (preview) {
+ // Only image previews allowed
+ unsigned int width = 0, height = 0;
+ if ((item->type == FileShareManifest::T_IMAGE)
+ && !query.empty()
+ && (sscanf(query.c_str(), "width=%u&height=%u",
+ &width, &height) == 2)) {
+ width = talk_base::_max<unsigned int>(1, talk_base::_min(width, kMaxPreviewSize));
+ height = talk_base::_max<unsigned int>(1, talk_base::_min(height, kMaxPreviewSize));
+ std::string pathname;
+ if (is_sender_) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.SetFilename(item->name);
+ pathname = local_path.pathname();
+ } else if ((item_index < stored_location_.size())
+ && !stored_location_[item_index].empty()) {
+ pathname = stored_location_[item_index];
+ }
+ if (!pathname.empty()) {
+ transactions_.push_back(transaction);
+ SignalResampleImage(pathname, width, height, transaction);
+ }
+ }
+ } else if (item->type == FileShareManifest::T_FOLDER) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.AppendFolder(item->name);
+ talk_base::TarStream* tar = new talk_base::TarStream;
+ VERIFY(tar->AddFilter(local_path.folder_name()));
+ if (tar->Open(local_path.parent_folder(), true)) {
+ stream = tar;
+ tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry);
+ mime_type = "application/x-tar";
+ } else {
+ delete tar;
+ }
+ } else if ((item->type == FileShareManifest::T_FILE)
+ || (item->type == FileShareManifest::T_IMAGE)) {
+ talk_base::Pathname local_path;
+ local_path.SetFolder(local_folder_);
+ local_path.SetFilename(item->name);
+ talk_base::FileStream* file = new talk_base::FileStream;
+ LOG(LS_INFO) << "opening file " << local_path.pathname();
+ if (file->Open(local_path.pathname().c_str(), "rb")) {
+ LOG(LS_INFO) << "File opened";
+ stream = file;
+ } else {
+ delete file;
+ }
+ }
+
+ if (!stream) {
+ transaction->response()->set_error(talk_base::HC_NOT_FOUND);
+ } else if (original) {
+ // We should never have more than one original request pending at a time
+ ASSERT(NULL == counter_);
+ StreamCounter* counter = new StreamCounter(stream);
+ counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes);
+ transaction->response()->set_success(mime_type.c_str(), counter);
+ transfer_connection_id_ = transaction->connection_id();
+ item_transferring_ = item_index;
+ counter_ = counter;
+ } else {
+ // Note: in the preview case, we don't set counter_, so the transferred
+ // bytes won't be shown as progress, and won't trigger a state change.
+ transaction->response()->set_success(mime_type.c_str(), stream);
+ }
+
+ LOG_F(LS_INFO) << "Result: " << transaction->response()->scode;
+ http_server_->Respond(transaction);
+}
+
+void FileShareSession::OnHttpRequestComplete(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction, int err) {
+ LOG_F(LS_INFO) << "(" << transaction->request()->path << ", " << err << ")";
+ ASSERT(server == http_server_);
+
+ // We only care about transferred originals
+ if (transfer_connection_id_ != transaction->connection_id())
+ return;
+
+ ASSERT(item_transferring_ < manifest_->size());
+ ASSERT(NULL != counter_);
+
+ transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID;
+ transfer_name_.clear();
+ counter_ = NULL;
+
+ if (err == 0) {
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ if (item.size == FileShareManifest::SIZE_UNKNOWN) {
+ bytes_transferred_ += 1;
+ } else {
+ bytes_transferred_ += item.size;
+ }
+ }
+}
+
+void FileShareSession::OnHttpConnectionClosed(talk_base::HttpServer* server,
+ int err, talk_base::StreamInterface* stream) {
+ LOG_F(LS_INFO) << "(" << err << ")";
+ talk_base::Thread::Current()->Dispose(stream);
+}
+
+// TarStream Signals
+
+void FileShareSession::OnNextEntry(const std::string& name, size_t size) {
+ LOG_F(LS_VERBOSE) << "(" << name << ", " << size << ")";
+ transfer_name_ = name;
+ SignalNextFile(this);
+}
+
+// Socket Signals
+
+void FileShareSession::OnProxyAccept(talk_base::AsyncSocket* socket) {
+ bool is_remote;
+ if (socket == remote_listener_) {
+ is_remote = true;
+ ASSERT(NULL != session_);
+ } else if (socket == local_listener_) {
+ is_remote = false;
+ } else {
+ ASSERT(false);
+ return;
+ }
+
+ while (talk_base::AsyncSocket* accepted =
+ static_cast<talk_base::AsyncSocket*>(socket->Accept(NULL))) {
+
+ // Check if connection is from localhost.
+ if (accepted->GetRemoteAddress().ip() != 0x7F000001) {
+ delete accepted;
+ continue;
+ }
+
+ LOG_F(LS_VERBOSE) << (is_remote ? "[remote]" : "[local]");
+
+ if (is_remote) {
+ char channel_name[64];
+ talk_base::sprintfn(channel_name, ARRAY_SIZE(channel_name),
+ "proxy-%u", next_channel_id_++);
+ talk_base::StreamInterface* remote =
+ (NULL != session_) ? CreateChannel(channel_name) : NULL;
+ if (!remote) {
+ LOG_F(LS_WARNING) << "CreateChannel(" << channel_name << ") failed";
+ delete accepted;
+ continue;
+ }
+
+ talk_base::StreamInterface* local = new talk_base::SocketStream(accepted);
+ StreamRelay* proxy = new StreamRelay(local, remote, 64 * 1024);
+ proxy->SignalClosed.connect(this, &FileShareSession::OnProxyClosed);
+ proxies_.push_back(proxy);
+ proxy->Circulate();
+ talk_base::Thread::Current()->Clear(this, MSG_PROXY_WAIT);
+ } else {
+ talk_base::StreamInterface* local = new talk_base::SocketStream(accepted);
+ http_server_->HandleConnection(local);
+ }
+ }
+}
+
+void FileShareSession::OnProxyClosed(StreamRelay* proxy, int error) {
+ ProxyList::iterator it = std::find(proxies_.begin(), proxies_.end(), proxy);
+ if (it == proxies_.end()) {
+ ASSERT(false);
+ return;
+ }
+
+ LOG_F(LS_VERBOSE) << "(" << error << ")";
+
+ proxies_.erase(it);
+ talk_base::Thread::Current()->Dispose(proxy);
+
+ if (proxies_.empty() && IsComplete() && !IsClosed()) {
+ talk_base::Thread::Current()->PostDelayed(kProxyWait, this, MSG_PROXY_WAIT);
+ }
+}
+
+
+void FileShareSession::OnUpdateBytes(size_t count) {
+ SignalUpdateProgress(this);
+}
+
+// Internal Helpers
+
+void FileShareSession::GenerateTemporaryPrefix(std::string* prefix) {
+ std::string data = cricket::CreateRandomString(32);
+ ASSERT(NULL != prefix);
+ prefix->assign("/temporary/");
+ prefix->append(talk_base::MD5(data));
+ prefix->append("/");
+}
+
+void FileShareSession::GetItemNetworkPath(size_t index, bool preview,
+ std::string* path) {
+ ASSERT(index < manifest_->size());
+ ASSERT(NULL != path);
+
+ // preview_path_ and source_path_ are url path segments, which are composed
+ // with the address of the localhost p2p proxy to provide a url which IE can
+ // use.
+
+ std::string ue_name;
+ const std::string& name = manifest_->item(index).name;
+ talk_base::transform(ue_name, name.length() * 3, name, talk_base::url_encode);
+
+ talk_base::Pathname pathname;
+ pathname.SetFolder(preview ? preview_path_ : source_path_);
+ pathname.SetFilename(ue_name);
+ *path = pathname.pathname();
+}
+
+bool FileShareSession::GetItemBaseUrl(size_t index, bool preview,
+ std::string* url) {
+ // This function composes a URL to the referenced item. It may be a local
+ // file url (file:///...), or a remote peer url relayed through localhost
+ // (http://...)
+
+ ASSERT(NULL != url);
+ if (index >= manifest_->size()) {
+ ASSERT(false);
+ return false;
+ }
+
+ const FileShareManifest::Item& item = manifest_->item(index);
+
+ bool is_remote;
+ if (is_sender_) {
+ if (!preview) {
+ talk_base::Pathname path(local_folder_);
+ path.SetFilename(item.name);
+ *url = path.url();
+ return true;
+ }
+ is_remote = false;
+ } else {
+ if ((index < stored_location_.size()) && !stored_location_[index].empty()) {
+ if (!preview) {
+ *url = talk_base::Pathname(stored_location_[index]).url();
+ return true;
+ }
+ // Note: Using the local downloaded files as a source for previews is
+ // desireable, because it means that previews can be regenerated if IE's
+ // cached versions get flushed for some reason, and the remote side is
+ // not available. However, it has the downside that IE _must_ regenerate
+ // the preview locally, which takes time, memory and CPU. Eventually,
+ // we will unify the remote and local cached copy through some sort of
+ // smart http proxying. In the meantime, always use the remote url, to
+ // eliminate the annoying transition from remote to local caching.
+ //is_remote = false;
+ is_remote = true;
+ } else {
+ is_remote = true;
+ }
+ }
+
+ talk_base::SocketAddress address;
+ if (!GetProxyAddress(address, is_remote))
+ return false;
+
+ std::string path;
+ GetItemNetworkPath(index, preview, &path);
+ talk_base::Url<char> make_url(path.c_str(),
+ address.IPAsString().c_str(),
+ address.port());
+ *url = make_url.url();
+ return true;
+}
+
+bool FileShareSession::GetProxyAddress(talk_base::SocketAddress& address,
+ bool is_remote) {
+ talk_base::AsyncSocket*& proxy_listener =
+ is_remote ? remote_listener_ : local_listener_;
+
+ if (!proxy_listener) {
+ talk_base::AsyncSocket* listener =
+ talk_base::Thread::Current()->socketserver()
+ ->CreateAsyncSocket(SOCK_STREAM);
+ if (!listener)
+ return false;
+
+ talk_base::SocketAddress bind_address("127.0.0.1", 0);
+
+ if ((listener->Bind(bind_address) != 0)
+ || (listener->Listen(5) != 0)) {
+ delete listener;
+ return false;
+ }
+
+ LOG(LS_INFO) << "Proxy listener available @ "
+ << listener->GetLocalAddress().ToString();
+
+ listener->SignalReadEvent.connect(this, &FileShareSession::OnProxyAccept);
+ proxy_listener = listener;
+ }
+
+ if (proxy_listener->GetState() == talk_base::Socket::CS_CLOSED) {
+ if (is_remote) {
+ address = remote_listener_address_;
+ return true;
+ }
+ return false;
+ }
+
+ address = proxy_listener->GetLocalAddress();
+ return !address.IsAny();
+}
+
+talk_base::StreamInterface* FileShareSession::CreateChannel(
+ const std::string& channel_name) {
+ ASSERT(NULL != session_);
+
+ // Send a heads-up for our new channel
+ cricket::Session::XmlElements els;
+ buzz::XmlElement* xel_channel = new buzz::XmlElement(QN_SHARE_CHANNEL, true);
+ xel_channel->AddAttr(buzz::QN_NAME, channel_name);
+ els.push_back(xel_channel);
+ session_->SendInfoMessage(els);
+
+ cricket::PseudoTcpChannel* channel =
+ new cricket::PseudoTcpChannel(talk_base::Thread::Current(), session_);
+ VERIFY(channel->Connect(channel_name));
+ return channel->GetStream();
+}
+
+void FileShareSession::SetState(FileShareState state, bool prevent_close) {
+ if (state == state_)
+ return;
+
+ if (IsComplete()) {
+ // Entering a completion state is permanent.
+ ASSERT(false);
+ return;
+ }
+
+ state_ = state;
+ if (IsComplete()) {
+ // All completion states auto-close except for FS_COMPLETE
+ bool close = (state_ > FS_COMPLETE) || !prevent_close;
+ if (close) {
+ DoClose(true);
+ }
+ }
+
+ SignalState(state_);
+}
+
+void FileShareSession::OnInitiate() {
+ // Cache the variables we will need, in case session_ goes away
+ is_sender_ = session_->initiator();
+ jid_ = buzz::Jid(session_->remote_name());
+ manifest_ = new FileShareManifest(description()->manifest);
+ source_path_ = description()->source_path;
+ preview_path_ = description()->preview_path;
+
+ if (local_folder_.empty()) {
+ LOG(LS_ERROR) << "FileShareSession - no local folder, using temp";
+ talk_base::Pathname temp_folder;
+ talk_base::Filesystem::GetTemporaryFolder(temp_folder, true, NULL);
+ local_folder_ = temp_folder.pathname();
+ }
+ LOG(LS_INFO) << session_->state();
+ SetState(FS_OFFER, false);
+}
+
+void FileShareSession::NextDownload() {
+ if (FS_TRANSFER != state_)
+ return;
+
+ if (item_transferring_ >= manifest_->size()) {
+ // Notify the other side that transfer has completed
+ cricket::Session::XmlElements els;
+ els.push_back(new buzz::XmlElement(QN_SHARE_COMPLETE, true));
+ session_->SendInfoMessage(els);
+ SetState(FS_COMPLETE, !proxies_.empty());
+ return;
+ }
+
+ const FileShareManifest::Item& item = manifest_->item(item_transferring_);
+ if ((item.type != FileShareManifest::T_FILE)
+ && (item.type != FileShareManifest::T_IMAGE)
+ && (item.type != FileShareManifest::T_FOLDER)) {
+ item_transferring_ += 1;
+ NextDownload();
+ return;
+ }
+
+ const bool is_folder = (item.type == FileShareManifest::T_FOLDER);
+ talk_base::Pathname temp_name;
+ temp_name.SetFilename(item.name);
+ if (!talk_base::CreateUniqueFile(temp_name, !is_folder)) {
+ SetState(FS_FAILURE, false);
+ return;
+ }
+
+ talk_base::StreamInterface* stream = NULL;
+ if (is_folder) {
+ // Convert unique filename into unique foldername
+ temp_name.AppendFolder(temp_name.filename());
+ temp_name.SetFilename("");
+ talk_base::TarStream* tar = new talk_base::TarStream;
+ // Note: the 'target' directory will be a subdirectory of the transfer_path_
+ talk_base::Pathname target;
+ target.SetFolder(item.name);
+ tar->AddFilter(target.pathname());
+ if (!tar->Open(temp_name.pathname(), false)) {
+ delete tar;
+ SetState(FS_FAILURE, false);
+ return;
+ }
+ stream = tar;
+ tar->SignalNextEntry.connect(this, &FileShareSession::OnNextEntry);
+ } else {
+ talk_base::FileStream* file = new talk_base::FileStream;
+ if (!file->Open(temp_name.pathname().c_str(), "wb")) {
+ delete file;
+ talk_base::Filesystem::DeleteFile(temp_name);
+ SetState(FS_FAILURE, false);
+ return;
+ }
+ stream = file;
+ }
+
+ ASSERT(NULL != stream);
+ transfer_path_ = temp_name.pathname();
+
+ std::string remote_path;
+ GetItemNetworkPath(item_transferring_, false, &remote_path);
+
+ StreamCounter* counter = new StreamCounter(stream);
+ counter->SignalUpdateByteCount.connect(this, &FileShareSession::OnUpdateBytes);
+ counter_ = counter;
+
+ http_client_->reset();
+ http_client_->set_server(talk_base::SocketAddress(jid_.Str(), 0, false));
+ http_client_->request().verb = talk_base::HV_GET;
+ http_client_->request().path = remote_path;
+ http_client_->response().document.reset(counter);
+ http_client_->start();
+}
+
+
+const FileShareSession::FileShareDescription* FileShareSession::description()
+const {
+ ASSERT(NULL != session_);
+ const cricket::SessionDescription* desc =
+ session_->initiator() ? session_->description()
+ : session_->remote_description();
+ return static_cast<const FileShareDescription*>(desc);
+}
+
+void FileShareSession::DoClose(bool terminate) {
+ ASSERT(!is_closed_);
+ ASSERT(IsComplete());
+ ASSERT(NULL != session_);
+
+ is_closed_ = true;
+
+ if (http_client_) {
+ http_client_->reset();
+ }
+ if (http_server_) {
+ http_server_->CloseAll(true);
+ // Currently, CloseAll doesn't result in OnHttpRequestComplete callback.
+ // If we change that, the following resetting won't be necessary.
+ transfer_connection_id_ = talk_base::HTTP_INVALID_CONNECTION_ID;
+ transfer_name_.clear();
+ counter_ = NULL;
+ }
+ // 'reset' and 'CloseAll' cause counter_ to clear.
+ ASSERT(NULL == counter_);
+
+ if (remote_listener_) {
+ // Cache the address for the remote_listener_, so that we can continue to
+ // present a consistent URL for remote previews, which is necessary for IE
+ // to continue using its cached copy.
+ remote_listener_address_ = remote_listener_->GetLocalAddress();
+ remote_listener_->Close();
+ LOG(LS_INFO) << "Proxy listener closed @ "
+ << remote_listener_address_.ToString();
+ }
+
+ if (terminate) {
+ session_->Terminate();
+ }
+}
+
+//////////////////////////////
+/// FileShareSessionClient //
+////////////////////////////
+
+void FileShareSessionClient::OnSessionCreate(cricket::Session* session,
+ bool received_initiate) {
+ VERIFY(sessions_.insert(session).second);
+ if (received_initiate) {
+ FileShareSession* share = new FileShareSession(session, user_agent_);
+ SignalFileShareSessionCreate(share);
+ UNUSED(share); // FileShareSession registers itself with the UI
+ }
+}
+
+void FileShareSessionClient::OnSessionDestroy(cricket::Session* session) {
+ VERIFY(1 == sessions_.erase(session));
+}
+
+const cricket::SessionDescription* FileShareSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ FileShareSession::FileShareDescription* share_desc =
+ new FileShareSession::FileShareDescription;
+
+ if (element->Name() != QN_SHARE_DESCRIPTION)
+ return share_desc;
+
+ const buzz::XmlElement* manifest = element->FirstNamed(QN_SHARE_MANIFEST);
+ const buzz::XmlElement* protocol = element->FirstNamed(QN_SHARE_PROTOCOL);
+
+ if (!manifest || !protocol)
+ return share_desc;
+
+ for (const buzz::XmlElement* item = manifest->FirstElement();
+ item != NULL; item = item->NextElement()) {
+ bool is_folder;
+ if (item->Name() == QN_SHARE_FOLDER) {
+ is_folder = true;
+ } else if (item->Name() == QN_SHARE_FILE) {
+ is_folder = false;
+ } else {
+ continue;
+ }
+ std::string name;
+ if (const buzz::XmlElement* el_name = item->FirstNamed(QN_SHARE_NAME)) {
+ name = el_name->BodyText();
+ }
+ if (name.empty()) {
+ continue;
+ }
+ size_t size = FileShareManifest::SIZE_UNKNOWN;
+ if (item->HasAttr(QN_SIZE)) {
+ size = strtoul(item->Attr(QN_SIZE).c_str(), NULL, 10);
+ }
+ if (is_folder) {
+ share_desc->manifest.AddFolder(name, size);
+ } else {
+ // Check if there is a valid image description for this file.
+ if (const buzz::XmlElement* image = item->FirstNamed(QN_SHARE_IMAGE)) {
+ if (image->HasAttr(QN_WIDTH) && image->HasAttr(QN_HEIGHT)) {
+ size_t width = strtoul(image->Attr(QN_WIDTH).c_str(), NULL, 10);
+ size_t height = strtoul(image->Attr(QN_HEIGHT).c_str(), NULL, 10);
+ if (AllowedImageDimensions(width, height)) {
+ share_desc->manifest.AddImage(name, size, width, height);
+ continue;
+ }
+ }
+ }
+ share_desc->manifest.AddFile(name, size);
+ }
+ }
+
+ if (const buzz::XmlElement* http = protocol->FirstNamed(QN_SHARE_HTTP)) {
+ share_desc->supports_http = true;
+ for (const buzz::XmlElement* url = http->FirstNamed(QN_SHARE_URL);
+ url != NULL; url = url->NextNamed(QN_SHARE_URL)) {
+ if (url->Attr(buzz::QN_NAME) == kHttpSourcePath) {
+ share_desc->source_path = url->BodyText();
+ } else if (url->Attr(buzz::QN_NAME) == kHttpPreviewPath) {
+ share_desc->preview_path = url->BodyText();
+ }
+ }
+ }
+
+ return share_desc;
+}
+
+buzz::XmlElement* FileShareSessionClient::TranslateSessionDescription(
+ const cricket::SessionDescription* description) {
+
+ const FileShareSession::FileShareDescription* share_desc =
+ static_cast<const FileShareSession::FileShareDescription*>(description);
+
+ scoped_ptr<buzz::XmlElement> el(new buzz::XmlElement(QN_SHARE_DESCRIPTION,
+ true));
+
+ const FileShareManifest& manifest = share_desc->manifest;
+ el->AddElement(new buzz::XmlElement(QN_SHARE_MANIFEST));
+ for (size_t i=0; i<manifest.size(); ++i) {
+ const FileShareManifest::Item& item = manifest.item(i);
+ buzz::QName qname;
+ if (item.type == FileShareManifest::T_FOLDER) {
+ qname = QN_SHARE_FOLDER;
+ } else if ((item.type == FileShareManifest::T_FILE)
+ || (item.type == FileShareManifest::T_IMAGE)) {
+ qname = QN_SHARE_FILE;
+ } else {
+ ASSERT(false);
+ continue;
+ }
+ el->AddElement(new buzz::XmlElement(qname), 1);
+ if (item.size != FileShareManifest::SIZE_UNKNOWN) {
+ char buffer[256];
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.size);
+ el->AddAttr(QN_SIZE, buffer, 2);
+ }
+ buzz::XmlElement* el_name = new buzz::XmlElement(QN_SHARE_NAME);
+ el_name->SetBodyText(item.name);
+ el->AddElement(el_name, 2);
+ if ((item.type == FileShareManifest::T_IMAGE)
+ && AllowedImageDimensions(item.width, item.height)) {
+ el->AddElement(new buzz::XmlElement(QN_SHARE_IMAGE), 2);
+ char buffer[256];
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.width);
+ el->AddAttr(QN_WIDTH, buffer, 3);
+ talk_base::sprintfn(buffer, sizeof(buffer), "%lu", item.height);
+ el->AddAttr(QN_HEIGHT, buffer, 3);
+ }
+ }
+
+ el->AddElement(new buzz::XmlElement(QN_SHARE_PROTOCOL));
+ if (share_desc->supports_http) {
+ el->AddElement(new buzz::XmlElement(QN_SHARE_HTTP), 1);
+ if (!share_desc->source_path.empty()) {
+ buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL);
+ url->SetAttr(buzz::QN_NAME, kHttpSourcePath);
+ url->SetBodyText(share_desc->source_path);
+ el->AddElement(url, 2);
+ }
+ if (!share_desc->preview_path.empty()) {
+ buzz::XmlElement* url = new buzz::XmlElement(QN_SHARE_URL);
+ url->SetAttr(buzz::QN_NAME, kHttpPreviewPath);
+ url->SetBodyText(share_desc->preview_path);
+ el->AddElement(url, 2);
+ }
+ }
+
+ return el.release();
+}
+
+FileShareSession *FileShareSessionClient::CreateFileShareSession() {
+ cricket::Session* session = sm_->CreateSession(jid_.Str(),
+ NS_GOOGLE_SHARE);
+ FileShareSession* share = new FileShareSession(session, user_agent_);
+ SignalFileShareSessionCreate(share);
+ return share;
+}
+
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h
new file mode 100644
index 0000000..b0802b4
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/fileshare/fileshare.h
@@ -0,0 +1,250 @@
+#ifndef TALK_APP_WIN32_FILESHARE_H__
+#define TALK_APP_WIN32_FILESHARE_H__
+#include "talk/base/messagequeue.h"
+#include "talk/base/socketpool.h"
+#include "talk/base/stringutils.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/xmpp/jid.h"
+
+class StreamCounter;
+class StreamRelay;
+
+namespace talk_base {
+ class HttpClient;
+ class HttpServer;
+ class HttpTransaction;
+}
+
+extern const std::string NS_GOOGLE_SHARE;
+
+
+namespace cricket {
+
+///////////////////////////////////////////////////////////////////////////////
+// FileShareManifest
+///////////////////////////////////////////////////////////////////////////////
+
+class FileShareManifest {
+public:
+ enum Type { T_FILE, T_IMAGE, T_FOLDER };
+ enum { SIZE_UNKNOWN = talk_base::SIZE_UNKNOWN };
+
+ struct Item {
+ Type type;
+ std::string name;
+ size_t size, width, height;
+ };
+ typedef std::vector<Item> ItemList;
+
+ inline bool empty() const { return items_.empty(); }
+ inline size_t size() const { return items_.size(); }
+ inline const Item& item(size_t index) const { return items_[index]; }
+
+ void AddFile(const std::string& name, size_t size);
+ void AddImage(const std::string& name, size_t size,
+ size_t width, size_t height);
+ void AddFolder(const std::string& name, size_t size);
+
+ size_t GetItemCount(Type t) const;
+ inline size_t GetFileCount() const { return GetItemCount(T_FILE); }
+ inline size_t GetImageCount() const { return GetItemCount(T_IMAGE); }
+ inline size_t GetFolderCount() const { return GetItemCount(T_FOLDER); }
+
+private:
+ ItemList items_;
+};
+
+
+enum FileShareState {
+ FS_NONE, // Initialization
+ FS_OFFER, // Offer extended
+ FS_TRANSFER, // In progress
+ FS_COMPLETE, // Completed successfully
+ FS_LOCAL_CANCEL, // Local side cancelled
+ FS_REMOTE_CANCEL, // Remote side cancelled
+ FS_FAILURE // An error occurred during transfer
+};
+
+
+class FileShareSession
+ : public talk_base::StreamPool,
+ public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ struct FileShareDescription : public cricket::SessionDescription {
+ FileShareManifest manifest;
+ bool supports_http;
+ std::string source_path;
+ std::string preview_path;
+ FileShareDescription() : supports_http(false) { }
+ };
+
+ FileShareSession(cricket::Session* session, const std::string &user_agent);
+ virtual ~FileShareSession();
+
+ bool IsComplete() const;
+ bool IsClosed() const;
+ FileShareState state() const;
+ sigslot::signal1<FileShareState> SignalState;
+ sigslot::signal1<FileShareSession*> SignalNextFile;
+ sigslot::signal1<FileShareSession*> SignalUpdateProgress;
+ sigslot::signal4<std::string, int, int, talk_base::HttpTransaction*> SignalResampleImage;
+
+ void ResampleComplete(talk_base::StreamInterface *si, talk_base::HttpTransaction *trans, bool success);
+
+ bool is_sender() const;
+ const buzz::Jid& jid() const;
+ const FileShareManifest* manifest() const;
+ const std::string& local_folder() const;
+
+ void SetLocalFolder(const std::string& folder) { local_folder_ = folder; }
+ void Share(const buzz::Jid& jid, FileShareManifest* manifest);
+
+ void Accept();
+ void Decline();
+ void Cancel();
+
+ bool GetItemUrl(size_t index, std::string* url);
+ bool GetImagePreviewUrl(size_t index, size_t width, size_t height,
+ std::string* url);
+ // Returns true if the transferring item size is known
+ bool GetProgress(size_t& bytes) const;
+ // Returns true if the total size is known
+ bool GetTotalSize(size_t& bytes) const;
+ // Returns true if currently transferring item name is known
+ bool GetCurrentItemName(std::string* name);
+
+ // TODO: Eliminate this eventually?
+ cricket::Session* session() { return session_; }
+
+ // StreamPool Interface
+ virtual talk_base::StreamInterface*
+ RequestConnectedStream(const talk_base::SocketAddress& remote, int* err);
+ virtual void ReturnConnectedStream(talk_base::StreamInterface* stream);
+
+ // MessageHandler Interface
+ virtual void OnMessage(talk_base::Message* msg);
+
+ void GetItemNetworkPath(size_t index, bool preview, std::string* path);
+
+private:
+ typedef std::list<StreamRelay*> ProxyList;
+ typedef std::list<talk_base::HttpTransaction*> TransactionList;
+
+ // Session Signals
+ void OnSessionState(cricket::Session* session, cricket::Session::State state);
+ void OnSessionInfoMessage(cricket::Session* session,
+ const cricket::Session::XmlElements& els);
+ void OnSessionChannelGone(cricket::Session* session,
+ const std::string& name);
+
+ // HttpClient Signals
+ void OnHttpClientComplete(talk_base::HttpClient* http, int err);
+ void OnHttpClientClosed(talk_base::HttpClient* http, int err);
+
+ // HttpServer Signals
+ void OnHttpRequest(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction);
+ void OnHttpRequestComplete(talk_base::HttpServer* server,
+ talk_base::HttpTransaction* transaction,
+ int err);
+ void OnHttpConnectionClosed(talk_base::HttpServer* server,
+ int err,
+ talk_base::StreamInterface* stream);
+
+ // TarStream Signals
+ void OnNextEntry(const std::string& name, size_t size);
+
+ // Socket Signals
+ void OnProxyAccept(talk_base::AsyncSocket* socket);
+ void OnProxyClosed(StreamRelay* proxy, int error);
+
+ // StreamCounterSignals
+ void OnUpdateBytes(size_t count);
+
+ // Internal Helpers
+ void GenerateTemporaryPrefix(std::string* prefix);
+ bool GetItemBaseUrl(size_t index, bool preview, std::string* url);
+ bool GetProxyAddress(talk_base::SocketAddress& address, bool is_remote);
+ talk_base::StreamInterface* CreateChannel(const std::string& channel_name);
+ void SetState(FileShareState state, bool prevent_close);
+ void OnInitiate();
+ void NextDownload();
+ const FileShareDescription* description() const;
+ void DoClose(bool terminate);
+
+ cricket::Session* session_;
+ FileShareState state_;
+ bool is_closed_;
+ bool is_sender_;
+ buzz::Jid jid_;
+ FileShareManifest* manifest_;
+ std::string source_path_;
+ std::string preview_path_;
+ std::string local_folder_;
+
+ // The currently active p2p streams to our peer
+ talk_base::StreamCache pool_;
+ // The http client state (client only)
+ talk_base::HttpClient* http_client_;
+ // The http server state (server only)
+ talk_base::HttpServer* http_server_;
+ // The connection id of the currently transferring file (server)
+ int transfer_connection_id_;
+ // The counter for the currently transferring file
+ const StreamCounter* counter_;
+ // The number of manifest items that have successfully transferred
+ size_t item_transferring_;
+ // The byte count of successfully transferred items
+ size_t bytes_transferred_;
+ // Where the currently transferring item is being (temporarily) saved (client)
+ std::string transfer_path_;
+ // The name of the currently transferring item
+ std::string transfer_name_;
+ // Where the files are saved after transfer (client)
+ std::vector<std::string> stored_location_;
+ // Was it a local cancel? Or a remote cancel?
+ bool local_cancel_;
+ // Proxy socket for local IE http requests
+ talk_base::AsyncSocket* local_listener_;
+ // Proxy socket for remote IE http requests
+ talk_base::AsyncSocket* remote_listener_;
+ // Cached address of remote_listener_
+ talk_base::SocketAddress remote_listener_address_;
+ // Uniqueness for channel names
+ size_t next_channel_id_;
+ // Proxy relays
+ ProxyList proxies_;
+ std::string user_agent_;
+ TransactionList transactions_;
+};
+
+class FileShareSessionClient : public SessionClient
+{
+ public:
+ FileShareSessionClient(SessionManager *sm, buzz::Jid jid, const std::string &user_agent) : sm_(sm), jid_(jid),
+ user_agent_(user_agent) {}
+ virtual void OnSessionCreate(cricket::Session* session,
+ bool received_initiate);
+ virtual void OnSessionDestroy(cricket::Session* session);
+ virtual const cricket::SessionDescription* CreateSessionDescription(const buzz::XmlElement* element);
+ virtual buzz::XmlElement* TranslateSessionDescription(const cricket::SessionDescription* description);
+ FileShareSession *CreateFileShareSession();
+
+ sigslot::signal1<FileShareSession*> SignalFileShareSessionCreate;
+ sigslot::signal1<FileShareSession*> SignalFileShareSessionDestroy;
+
+ private:
+ SessionManager *sm_;
+ buzz::Jid jid_;
+ friend class FileShareSession;
+ typedef std::set<cricket::Session*> SessionSet;
+ SessionSet sessions_;
+ std::string user_agent_;
+};
+
+} // namespace cricket
+
+#endif // TALK_APP_WIN32_FILESHARE_H__
diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.am b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am
new file mode 100644
index 0000000..5d489b6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.am
@@ -0,0 +1,27 @@
+EXTRA_DIST=gipslitemediaengine.cc gipslitemediaengine.h
+if GIPS
+nodist_libcricketsessionphone_la_SOURCES=gipsmediaengine.cc \
+ gipsstatsmonitor.cc
+else
+dist_libcricketsessionphone_la_SOURCES=linphonemediaengine.cc
+endif
+
+libcricketsessionphone_la_SOURCES = audiomonitor.cc \
+ channelmanager.cc \
+ voicechannel.cc \
+ call.cc \
+ phonesessionclient.cc
+
+noinst_HEADERS = audiomonitor.h \
+ channelmanager.h \
+ linphonemediaengine.h \
+ mediaengine.h \
+ phonesessionclient.h \
+ voicechannel.h \
+ call.h \
+ mediachannel.h \
+ codec.h
+
+AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS)
+noinst_LTLIBRARIES = libcricketsessionphone.la
+
diff --git a/Plugins/jingle/libjingle/talk/session/phone/Makefile.in b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in
new file mode 100644
index 0000000..8dfdbc8
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/Makefile.in
@@ -0,0 +1,485 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/phone
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessionphone_la_LIBADD =
+am_libcricketsessionphone_la_OBJECTS = audiomonitor.lo \
+ channelmanager.lo voicechannel.lo call.lo \
+ phonesessionclient.lo
+am__dist_libcricketsessionphone_la_SOURCES_DIST = \
+ linphonemediaengine.cc
+@GIPS_FALSE@dist_libcricketsessionphone_la_OBJECTS = \
+@GIPS_FALSE@ linphonemediaengine.lo
+@GIPS_TRUE@nodist_libcricketsessionphone_la_OBJECTS = \
+@GIPS_TRUE@ gipsmediaengine.lo gipsstatsmonitor.lo
+libcricketsessionphone_la_OBJECTS = \
+ $(am_libcricketsessionphone_la_OBJECTS) \
+ $(dist_libcricketsessionphone_la_OBJECTS) \
+ $(nodist_libcricketsessionphone_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessionphone_la_SOURCES) \
+ $(dist_libcricketsessionphone_la_SOURCES) \
+ $(nodist_libcricketsessionphone_la_SOURCES)
+DIST_SOURCES = $(libcricketsessionphone_la_SOURCES) \
+ $(am__dist_libcricketsessionphone_la_SOURCES_DIST)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+EXTRA_DIST = gipslitemediaengine.cc gipslitemediaengine.h
+@GIPS_TRUE@nodist_libcricketsessionphone_la_SOURCES = gipsmediaengine.cc \
+@GIPS_TRUE@ gipsstatsmonitor.cc
+
+@GIPS_FALSE@dist_libcricketsessionphone_la_SOURCES = linphonemediaengine.cc
+libcricketsessionphone_la_SOURCES = audiomonitor.cc \
+ channelmanager.cc \
+ voicechannel.cc \
+ call.cc \
+ phonesessionclient.cc
+
+noinst_HEADERS = audiomonitor.h \
+ channelmanager.h \
+ linphonemediaengine.h \
+ mediaengine.h \
+ phonesessionclient.h \
+ voicechannel.h \
+ call.h \
+ mediachannel.h \
+ codec.h
+
+AM_CPPFLAGS := -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(top_srcdir)/talk/third_party/mediastreamer $(GLIB_CFLAGS)
+noinst_LTLIBRARIES = libcricketsessionphone.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/phone/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/phone/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessionphone.la: $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessionphone_la_LDFLAGS) $(libcricketsessionphone_la_OBJECTS) $(libcricketsessionphone_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audiomonitor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/call.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/channelmanager.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsmediaengine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gipsstatsmonitor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linphonemediaengine.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/phonesessionclient.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/voicechannel.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc
new file mode 100644
index 0000000..c1f3614
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.cc
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/voicechannel.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+AudioMonitor::AudioMonitor(VoiceChannel *voice_channel,
+ talk_base::Thread *monitor_thread) {
+ voice_channel_ = voice_channel;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+AudioMonitor::~AudioMonitor() {
+ voice_channel_->worker_thread()->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void AudioMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 100)
+ rate_ = 100;
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START);
+}
+
+void AudioMonitor::Stop() {
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP);
+}
+
+void AudioMonitor::OnMessage(talk_base::Message *message) {
+ talk_base::CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ PollVoiceChannel();
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ voice_channel_->worker_thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+ PollVoiceChannel();
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(talk_base::Thread::Current() == monitoring_thread_);
+ AudioInfo info = audio_info_;
+ crit_.Leave();
+ SignalUpdate(this, audio_info_);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void AudioMonitor::PollVoiceChannel() {
+ talk_base::CritScope cs(&crit_);
+ assert(talk_base::Thread::Current() == voice_channel_->worker_thread());
+
+ // Gather connection infos
+ audio_info_.input_level = voice_channel_->GetInputLevel_w();
+ audio_info_.output_level = voice_channel_->GetOutputLevel_w();
+
+ // Signal the monitoring thread, start another poll timer
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+VoiceChannel *AudioMonitor::voice_channel() {
+ return voice_channel_;
+}
+
+talk_base::Thread *AudioMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h
new file mode 100644
index 0000000..2bfd784
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/audiomonitor.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _CRICKET_PHONE_AUDIOMONITOR_H_
+#define _CRICKET_PHONE_AUDIOMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+
+struct AudioInfo {
+ int input_level;
+ int output_level;
+};
+
+class AudioMonitor : public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ AudioMonitor(VoiceChannel* voice_channel, talk_base::Thread *monitor_thread);
+ ~AudioMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ VoiceChannel* voice_channel();
+ talk_base::Thread *monitor_thread();
+
+ sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate;
+
+protected:
+ void OnMessage(talk_base::Message *message);
+ void PollVoiceChannel();
+
+ AudioInfo audio_info_;
+ VoiceChannel* voice_channel_;
+ talk_base::Thread* monitoring_thread_;
+ talk_base::CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _CRICKET_PHONE_AUDIOMONITOR_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.cc b/Plugins/jingle/libjingle/talk/session/phone/call.cc
new file mode 100644
index 0000000..1676945
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/call.cc
@@ -0,0 +1,336 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/session/phone/call.h"
+
+namespace cricket {
+
+const uint32 MSG_CHECKAUTODESTROY = 1;
+const uint32 MSG_TERMINATECALL = 2;
+
+namespace {
+const int kSendToVoicemailTimeout = 1000*20;
+const int kNoVoicemailTimeout = 1000*180;
+const int kMediaMonitorInterval = 1000*15;
+}
+
+Call::Call(PhoneSessionClient *session_client)
+ : muted_(false), send_to_voicemail_(true)
+{
+ session_client_ = session_client;
+ id_ = CreateRandomId();
+}
+
+Call::~Call() {
+ while (sessions_.begin() != sessions_.end()) {
+ Session *session = sessions_[0];
+ RemoveSession(session);
+ session_client_->session_manager()->DestroySession(session);
+ }
+ talk_base::Thread::Current()->Clear(this);
+}
+
+Session *Call::InitiateSession(const buzz::Jid &jid,
+ std::vector<buzz::XmlElement*>* extra_xml) {
+ Session *session = session_client_->CreateSession(this);
+ AddSession(session);
+ session->Initiate(jid.Str(), extra_xml,
+ session_client_->CreateOfferSessionDescription());
+
+ // After this timeout, terminate the call because the callee isn't
+ // answering
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ session_client_->session_manager()->signaling_thread()->PostDelayed(
+ send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout,
+ this, MSG_TERMINATECALL);
+ return session;
+}
+
+void Call::AcceptSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end()) {
+ session->Accept(session_client_->CreateAcceptSessionDescription(
+ session->remote_description()));
+ }
+}
+
+void Call::RedirectSession(Session *session, const buzz::Jid &to) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Redirect(to.Str());
+
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ session_client_->session_manager()->signaling_thread()->PostDelayed(
+ send_to_voicemail_ ? kSendToVoicemailTimeout : kNoVoicemailTimeout,
+ this, MSG_TERMINATECALL);
+}
+
+void Call::RejectSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Reject();
+}
+
+void Call::TerminateSession(Session *session) {
+ assert(std::find(sessions_.begin(), sessions_.end(), session)
+ != sessions_.end());
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it != sessions_.end())
+ (*it)->Terminate();
+}
+
+void Call::Terminate() {
+ // Copy the list so that we can iterate over it in a stable way
+ std::vector<Session *> sessions = sessions_;
+
+ // There may be more than one session to terminate
+ std::vector<Session *>::iterator it;
+ for (it = sessions.begin(); it != sessions.end(); it++)
+ TerminateSession(*it);
+
+}
+
+void Call::OnMessage(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CHECKAUTODESTROY:
+ // If no more sessions for this call, delete it
+ if (sessions_.size() == 0)
+ session_client_->DestroyCall(this);
+ break;
+ case MSG_TERMINATECALL:
+ // Signal to the user that a timeout has happened and the call should
+ // be sent to voicemail.
+ if (send_to_voicemail_) {
+ SignalSetupToCallVoicemail();
+ }
+
+ // Callee didn't answer - terminate call
+ Terminate();
+ break;
+ }
+}
+
+const std::vector<Session *> &Call::sessions() {
+ return sessions_;
+}
+
+void Call::AddSession(Session *session) {
+ // Add session to list, create voice channel for this session
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ VoiceChannel *channel
+ = session_client_->channel_manager()->CreateVoiceChannel(session);
+ channel_map_[session->id()] = channel;
+
+ // Start the media monitor for this voicechannel
+ channel->SignalMediaMonitor.connect(this, &Call::OnMediaMonitor);
+ channel->StartMediaMonitor(kMediaMonitorInterval);
+
+ // If this call has the focus, enable this channel
+ if (session_client_->GetFocus() == this)
+ channel->Enable(true);
+
+ // Signal client
+ SignalAddSession(this, session);
+}
+
+void Call::RemoveSession(Session *session) {
+ // Remove session from list
+ std::vector<Session *>::iterator it_session;
+ it_session = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it_session == sessions_.end())
+ return;
+ sessions_.erase(it_session);
+
+ // Destroy session channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = it_channel->second;
+ channel_map_.erase(it_channel);
+ channel->StopMediaMonitor();
+ session_client_->channel_manager()->DestroyVoiceChannel(channel);
+ }
+
+ // Signal client
+ SignalRemoveSession(this, session);
+
+
+
+ // The call auto destroys when the lass session is removed
+ talk_base::Thread::Current()->Post(this, MSG_CHECKAUTODESTROY);
+}
+
+VoiceChannel* Call::GetChannel(Session* session) {
+ std::map<SessionID, VoiceChannel *>::iterator it
+ = channel_map_.find(session->id());
+ assert(it != channel_map_.end());
+ return it->second;
+}
+
+void Call::EnableChannels(bool enable) {
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Enable(enable);
+ }
+}
+
+void Call::Mute(bool mute) {
+ muted_ = mute;
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Mute(mute);
+ }
+}
+
+
+void Call::Join(Call *call, bool enable) {
+ while (call->sessions_.size() != 0) {
+ // Move session
+ Session *session = call->sessions_[0];
+ call->sessions_.erase(call->sessions_.begin());
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+ session->SignalReceivedTerminateReason
+ .connect(this, &Call::OnReceivedTerminateReason);
+
+ // Move channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = call->channel_map_.find(session->id());
+ if (it_channel != call->channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ call->channel_map_.erase(it_channel);
+ channel_map_[session->id()] = channel;
+ channel->Enable(enable);
+ }
+ }
+}
+
+void Call::StartConnectionMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor);
+ channel->StartConnectionMonitor(cms);
+ }
+}
+
+void Call::StopConnectionMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopConnectionMonitor();
+ channel->SignalConnectionMonitor.disconnect(this);
+ }
+}
+
+void Call::StartAudioMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor);
+ channel->StartAudioMonitor(cms);
+ }
+}
+
+void Call::StopAudioMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopAudioMonitor();
+ channel->SignalAudioMonitor.disconnect(this);
+ }
+}
+
+
+void Call::OnConnectionMonitor(VoiceChannel *channel,
+ const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, channel->session(), infos);
+}
+
+void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) {
+ SignalAudioMonitor(this, channel->session(), info);
+}
+
+void Call::OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info) {
+ SignalMediaMonitor(this, channel->session(), info);
+}
+
+uint32 Call::id() {
+ return id_;
+}
+
+void Call::OnSessionState(Session *session, Session::State state) {
+ switch (state) {
+ case Session::STATE_RECEIVEDACCEPT:
+ case Session::STATE_RECEIVEDREJECT:
+ case Session::STATE_RECEIVEDTERMINATE:
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ break;
+ }
+ SignalSessionState(this, session, state);
+}
+
+void Call::OnSessionError(Session *session, Session::Error error) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalSessionError(this, session, error);
+}
+
+void Call::OnReceivedTerminateReason(Session *session, const std::string &reason) {
+ session_client_->session_manager()->signaling_thread()->Clear(this,
+ MSG_TERMINATECALL);
+ SignalReceivedTerminateReason(this, session, reason);
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/call.h b/Plugins/jingle/libjingle/talk/session/phone/call.h
new file mode 100644
index 0000000..7b8d9b6
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/call.h
@@ -0,0 +1,116 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _CALL_H_
+#define _CALL_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/xmpp/jid.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/audiomonitor.h"
+
+#include <map>
+#include <vector>
+#include <deque>
+
+namespace cricket {
+
+class PhoneSessionClient;
+
+class Call : public talk_base::MessageHandler, public sigslot::has_slots<> {
+public:
+ Call(PhoneSessionClient *session_client);
+ ~Call();
+
+ Session *InitiateSession(const buzz::Jid &jid,
+ std::vector<buzz::XmlElement*>* extra_xml);
+ void AcceptSession(Session *session);
+ void RedirectSession(Session *session, const buzz::Jid &to);
+ void RejectSession(Session *session);
+ void TerminateSession(Session *session);
+ void Terminate();
+ void StartConnectionMonitor(Session *session, int cms);
+ void StopConnectionMonitor(Session *session);
+ void StartAudioMonitor(Session *session, int cms);
+ void StopAudioMonitor(Session *session);
+ void Mute(bool mute);
+
+
+ const std::vector<Session *> &sessions();
+ uint32 id();
+ bool muted() const { return muted_; }
+
+ // Setting this to false will cause the call to have a longer timeout and
+ // for the SignalSetupToCallVoicemail to never fire.
+ void set_send_to_voicemail(bool send_to_voicemail) {
+ send_to_voicemail_ = send_to_voicemail;
+ }
+ bool send_to_voicemail() { return send_to_voicemail_; }
+
+ // Sets a flag on the chatapp that will redirect the call to voicemail once
+ // the call has been terminated
+ sigslot::signal0<> SignalSetupToCallVoicemail;
+ sigslot::signal2<Call *, Session *> SignalAddSession;
+ sigslot::signal2<Call *, Session *> SignalRemoveSession;
+ sigslot::signal3<Call *, Session *, Session::State> SignalSessionState;
+ sigslot::signal3<Call *, Session *, Session::Error> SignalSessionError;
+ sigslot::signal3<Call *, Session *, const std::string &> SignalReceivedTerminateReason;
+ sigslot::signal3<Call *, Session *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor;
+ sigslot::signal3<Call *, Session *, const AudioInfo&> SignalAudioMonitor;
+ sigslot::signal3<Call *, Session *, const MediaInfo&> SignalMediaMonitor;
+
+private:
+ void OnMessage(talk_base::Message *message);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionError(Session *session, Session::Error error);
+ void OnReceivedTerminateReason(Session *session, const std::string &reason);
+ void AddSession(Session *session);
+ void RemoveSession(Session *session);
+ void EnableChannels(bool enable);
+ void Join(Call *call, bool enable);
+ void OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info);
+ void OnMediaMonitor(VoiceChannel *channel, const MediaInfo& info);
+ VoiceChannel* GetChannel(Session* session);
+
+ uint32 id_;
+ PhoneSessionClient *session_client_;
+ std::vector<Session *> sessions_;
+ std::map<SessionID, VoiceChannel *> channel_map_;
+ bool muted_;
+ bool send_to_voicemail_;
+
+
+ friend class PhoneSessionClient;
+};
+
+}
+
+#endif // _CALL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc
new file mode 100644
index 0000000..77b7a73
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.cc
@@ -0,0 +1,219 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 HAVE_GIPS
+#include "talk/session/phone/gipsmediaengine.h"
+#elif HAVE_GIPSLITE
+#include "talk/session/phone/gipslitemediaengine.h"
+#else
+#include "talk/session/phone/linphonemediaengine.h"
+#endif
+#include "channelmanager.h"
+#include <cassert>
+#include <iostream>
+namespace cricket {
+
+const uint32 MSG_CREATEVOICECHANNEL = 1;
+const uint32 MSG_DESTROYVOICECHANNEL = 2;
+const uint32 MSG_SETAUDIOOPTIONS = 3;
+
+ChannelManager::ChannelManager(talk_base::Thread *worker_thread) {
+#ifdef HAVE_GIPS
+ media_engine_ = new GipsMediaEngine();
+#elif HAVE_GIPSLITE
+ media_engine_ = new GipsLiteMediaEngine();
+#else
+ media_engine_ = new LinphoneMediaEngine();
+#endif
+ worker_thread_ = worker_thread;
+ initialized_ = false;
+ Init();
+}
+
+ChannelManager::~ChannelManager() {
+ Exit();
+}
+
+MediaEngine *ChannelManager::media_engine() {
+ return media_engine_;
+}
+
+bool ChannelManager::Init() {
+ initialized_ = media_engine_->Init();
+ return initialized_;
+}
+
+void ChannelManager::Exit() {
+ if (!initialized_)
+ return;
+
+ // Need to destroy the voice channels
+
+ while (true) {
+ crit_.Enter();
+ VoiceChannel *channel = NULL;
+ if (channels_.begin() != channels_.end())
+ channel = channels_[0];
+ crit_.Leave();
+ if (channel == NULL)
+ break;
+ delete channel;
+ }
+ media_engine_->Terminate();
+}
+
+struct CreateParams {
+ Session *session;
+ VoiceChannel *channel;
+};
+
+VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) {
+ CreateParams params;
+ params.session = session;
+ params.channel = NULL;
+ talk_base::TypedMessageData<CreateParams *> data(&params);
+ worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data);
+ return params.channel;
+}
+
+VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) {
+ talk_base::CritScope cs(&crit_);
+
+ // This is ok to alloc from a thread other than the worker thread
+ assert(initialized_);
+ MediaChannel *channel = media_engine_->CreateChannel();
+ if (channel == NULL)
+ return NULL;
+
+ VoiceChannel *voice_channel = new VoiceChannel(this, session, channel);
+ channels_.push_back(voice_channel);
+ return voice_channel;
+}
+
+void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) {
+ talk_base::TypedMessageData<VoiceChannel *> data(voice_channel);
+ worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data);
+}
+
+void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) {
+ talk_base::CritScope cs(&crit_);
+ // Destroy voice channel.
+ assert(initialized_);
+ std::vector<VoiceChannel *>::iterator it = std::find(channels_.begin(),
+ channels_.end(), voice_channel);
+ assert(it != channels_.end());
+ if (it == channels_.end())
+ return;
+
+ channels_.erase(it);
+ MediaChannel *channel = voice_channel->channel();
+ delete voice_channel;
+ delete channel;
+}
+
+void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ AudioOptions options;
+ options.auto_gain_control = auto_gain_control;
+ options.wave_in_device = wave_in_device;
+ options.wave_out_device = wave_out_device;
+ talk_base::TypedMessageData<AudioOptions> data(options);
+ worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data);
+}
+
+void ChannelManager::SetAudioOptions_w(AudioOptions options) {
+ assert(worker_thread_ == talk_base::Thread::Current());
+
+ // Set auto gain control on
+ if (media_engine_->SetAudioOptions(
+ options.auto_gain_control ? MediaEngine::AUTO_GAIN_CONTROL : 0) != 0) {
+ // TODO: We need to log these failures.
+ }
+
+ // Set the audio devices
+ // This will fail if audio is already playing. Stop all of the media
+ // start it up again after changing the setting.
+ {
+ talk_base::CritScope cs(&crit_);
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->PauseMedia_w();
+ }
+
+ if (media_engine_->SetSoundDevices(options.wave_in_device,
+ options.wave_out_device) == -1) {
+ // TODO: We need to log these failures.
+ }
+
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->UnpauseMedia_w();
+ }
+ }
+}
+
+talk_base::Thread *ChannelManager::worker_thread() {
+ return worker_thread_;
+}
+
+void ChannelManager::OnMessage(talk_base::Message *message) {
+ switch (message->message_id) {
+ case MSG_CREATEVOICECHANNEL:
+ {
+ talk_base::TypedMessageData<CreateParams *> *data
+ = static_cast<talk_base::TypedMessageData<CreateParams *> *>(
+ message->pdata);
+ data->data()->channel = CreateVoiceChannel_w(data->data()->session);
+ }
+ break;
+
+ case MSG_DESTROYVOICECHANNEL:
+ {
+ talk_base::TypedMessageData<VoiceChannel *> *data
+ = static_cast<talk_base::TypedMessageData<VoiceChannel *> *>(
+ message->pdata);
+ DestroyVoiceChannel_w(data->data());
+ }
+ break;
+ case MSG_SETAUDIOOPTIONS:
+ {
+ talk_base::TypedMessageData<AudioOptions> *data
+ = static_cast<talk_base::TypedMessageData<AudioOptions> *>(
+ message->pdata);
+ SetAudioOptions_w(data->data());
+ }
+ break;
+ }
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h
new file mode 100644
index 0000000..c17d5bb
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/channelmanager.h
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _CHANNELMANAGER_H_
+#define _CHANNELMANAGER_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/mediaengine.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+class ChannelManager : public talk_base::MessageHandler {
+public:
+ ChannelManager(talk_base::Thread *worker_thread);
+ ~ChannelManager();
+
+ VoiceChannel *CreateVoiceChannel(Session *session);
+ void DestroyVoiceChannel(VoiceChannel *voice_channel);
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device);
+
+ MediaEngine *media_engine();
+ talk_base::Thread *worker_thread();
+
+private:
+ VoiceChannel *CreateVoiceChannel_w(Session *session);
+ void DestroyVoiceChannel_w(VoiceChannel *voice_channel);
+ void OnMessage(talk_base::Message *message);
+ bool Init();
+ void Exit();
+
+ struct AudioOptions {
+ bool auto_gain_control;
+ int wave_in_device;
+ int wave_out_device;
+ };
+ void SetAudioOptions_w(AudioOptions options);
+
+ talk_base::Thread *worker_thread_;
+ MediaEngine *media_engine_;
+ bool initialized_;
+ talk_base::CriticalSection crit_;
+
+ typedef std::vector<VoiceChannel*> VoiceChannels;
+ VoiceChannels channels_;
+};
+
+}
+
+#endif // _CHANNELMANAGER_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/codec.h b/Plugins/jingle/libjingle/talk/session/phone/codec.h
new file mode 100644
index 0000000..ef99c2b
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/codec.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+
+#ifndef _CODEC_H_
+#define _CODEC_H_
+
+struct Codec {
+ int id;
+ std::string name;
+ int clockrate;
+ int bitrate;
+ int channels;
+
+ int preference;
+
+ // Creates a codec with the given parameters.
+ Codec(int pt, const std::string& nm, int cr, int br, int cs, int pr) :
+ id(pt), name(nm), clockrate(cr), preference(pr), bitrate(br), channels(cs) {}
+ // Ranks codecs by their preferences.
+};
+
+#endif // CODEC_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc
new file mode 100644
index 0000000..6609790
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.cc
@@ -0,0 +1,239 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+#include "talk/base/logging.h"
+#include <cassert>
+#include <iostream>
+#include "gipslitemediaengine.h"
+using namespace cricket;
+
+#if 0
+#define TRACK(x) LOG(LS_VERBOSE) << x
+#else
+#define TRACK(x)
+#endif
+
+//#define GIPS_TRACING
+
+namespace {
+struct CodecPref { const char* name; int clockrate; int pref; };
+const CodecPref kGIPSCodecPrefs[] = {
+ { "ISAC", 1600, 7 },
+ { "speex", 1600, 6 },
+ { "IPCMWB", 1600, 6},
+ { "speex", 8000, 4},
+ { "iLBC", 8000, 1 },
+ { "G723", 8000, 4 },
+ { "EG711U", 8000, 3 },
+ { "EG711A", 8000, 3 },
+ { "PCMU", 8000, 2 },
+ { "PCMA", 8000, 2 },
+ { "CN", 8000, 2 },
+ { "red", 8000, -1 },
+ { "telephone-event", 8000, -1 }
+};
+const size_t kNumGIPSCodecs = sizeof(kGIPSCodecPrefs) / sizeof(CodecPref);
+}
+
+
+void GipsLiteMediaChannel::SetCodecs(const std::vector<Codec> &codecs) {
+ GIPS_CodecInst c;
+ std::vector<Codec>::const_iterator i;
+
+
+ bool first = true;
+ for (i = codecs.begin(); i < codecs.end(); i++) {
+ if (engine_->FindGIPSCodec(*i, &c) == false)
+ continue;
+
+ if (c.pltype != i->id) {
+ c.pltype = i->id;
+ engine_->gips().GIPSVE_SetRecPayloadType(gips_channel_, &c);
+ }
+
+ if (first) {
+ LOG(LS_INFO) << "Using " << c.plname << "/" << c.plfreq;
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ first = false;
+ }
+ }
+ if (first) {
+ // We're being asked to set an empty list of codecs. This will only happen when
+ // dealing with a buggy client. We'll send them the most common format: PCMU
+ Codec codec(0, "PCMU", 8000, 0, 1, 0);
+ LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
+ engine_->FindGIPSCodec(codec, &c);
+ engine_->gips().GIPSVE_SetSendCodec(gips_channel_, &c);
+ }
+}
+
+
+void GipsLiteMediaChannel::OnPacketReceived(const void *data, int len) {
+ engine_->gips().GIPSVE_ReceivedRTPPacket(gips_channel_, data, (int)len);
+}
+
+void GipsLiteMediaChannel::SetPlayout(bool playout) {
+ if (playout)
+ engine_->gips().GIPSVE_StartPlayout(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopPlayout(gips_channel_);
+}
+
+void GipsLiteMediaChannel::SetSend(bool send) {
+ if (send)
+ engine_->gips().GIPSVE_StartSend(gips_channel_);
+ else
+ engine_->gips().GIPSVE_StopSend(gips_channel_);
+}
+
+GipsLiteMediaChannel::GipsLiteMediaChannel(GipsLiteMediaEngine *engine) {
+ network_interface_ = NULL;
+ engine_ = engine;
+ gips_channel_ = engine_->gips().GIPSVE_CreateChannel();
+ engine_->gips().GIPSVE_SetSendTransport(gips_channel_, *this);
+}
+
+
+int GipsLiteMediaEngine::GetGIPSCodecPreference(const char *name, int clockrate) {
+ for (size_t i = 0; i < kNumGIPSCodecs; ++i) {
+ if ((strcmp(kGIPSCodecPrefs[i].name, name) == 0) &&
+ (kGIPSCodecPrefs[i].clockrate == clockrate))
+ return kGIPSCodecPrefs[i].pref;
+ }
+ assert(false);
+ return -1;
+}
+
+GipsLiteMediaEngine::GipsLiteMediaEngine() :
+ gips_(GetGipsVoiceEngineLite()) {}
+
+bool GipsLiteMediaEngine::Init() {
+
+ TRACK("GIPSVE_Init");
+ if (gips_.GIPSVE_Init() == -1)
+ return false;
+
+ char buffer[1024];
+ TRACK("GIPSVE_GetVersion");
+ int r = gips_.GIPSVE_GetVersion(buffer, sizeof(buffer));
+ LOG(LS_INFO) << "GIPS Version: " << r << ": " << buffer;
+
+ // Set auto gain control on
+ TRACK("GIPSVE_SetAGCStatus");
+ if (gips_.GIPSVE_SetAGCStatus(1) == -1)
+ return false;
+
+ TRACK("GIPSVE_GetNofCodecs");
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gips_codec;
+ if (gips_.GIPSVE_GetCodec(i, &gips_codec) >= 0) {
+ Codec codec(gips_codec.pltype, gips_codec.plname, gips_codec.plfreq, gips_codec.rate,
+ gips_codec.channels, GetGIPSCodecPreference(gips_codec.plname, gips_codec.plfreq));
+ LOG(LS_INFO) << gips_codec.plname << "/" << gips_codec.plfreq << "/" << gips_codec.channels << " " << gips_codec.pltype;
+ codecs_.push_back(codec);
+ }
+ }
+ return true;
+}
+
+void GipsLiteMediaEngine::Terminate() {
+ gips_.GIPSVE_Terminate();
+}
+
+MediaChannel * GipsLiteMediaEngine::CreateChannel() {
+ return new GipsLiteMediaChannel(this);
+}
+
+bool GipsLiteMediaEngine::FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec) {
+ int ncodecs = gips_.GIPSVE_GetNofCodecs();
+ for (int i = 0; i < ncodecs; ++i) {
+ GIPS_CodecInst gc;
+ if (gips_.GIPSVE_GetCodec(i, &gc) >= 0) {
+ if (codec.id < 96) {
+ // Compare by id
+ if (codec.id != gc.pltype)
+ continue;
+ } else {
+ // Compare by name
+ if (strcmp(codec.name.c_str(), gc.plname) != 0)
+ continue;
+ }
+
+ // If the clockrate is specified, make sure it matches
+ if (codec.clockrate > 0 && codec.clockrate != gc.plfreq)
+ continue;
+
+ // If the bitrate is specified, make sure it matches
+ if (codec.bitrate > 0 && codec.bitrate != gc.rate)
+ continue;
+
+ // Make sure the channels match
+ if (codec.channels != gc.channels)
+ continue;
+
+ // If we got this far, we match.
+ if (gips_codec)
+ *gips_codec = gc;
+ return true;
+ }
+ }
+ return false;
+}
+
+int GipsLiteMediaEngine::SetAudioOptions(int options) {
+ // Set auto gain control on
+ if (gips_.GIPSVE_SetAGCStatus(options & AUTO_GAIN_CONTROL ? 1 : 0) == -1) {
+ return -1;
+ // TODO: We need to log these failures.
+ }
+ return 0;
+}
+
+int GipsLiteMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) {
+ if (gips_.GIPSVE_SetSoundDevices(wave_in_device, wave_out_device) == -1) {
+ int error = gips_.GIPSVE_GetLastError();
+ // TODO: We need to log these failures.
+ return error;
+ }
+ return 0;
+}
+
+bool GipsLiteMediaEngine::FindCodec(const Codec &codec)
+{
+ return FindGIPSCodec(codec, NULL);
+}
+
+std::vector<Codec> GipsLiteMediaEngine::codecs()
+{
+ return codecs_;
+} \ No newline at end of file
diff --git a/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h
new file mode 100644
index 0000000..56e1f2e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/gipslitemediaengine.h
@@ -0,0 +1,93 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+// GipsLiteMediaEngine is a GIPS Voice Engine Lite implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
+
+#include "talk/third_party/gips/Interface/GipsVoiceEngineLite.h"
+#include "talk/third_party/gips/expiration.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class GipsLiteMediaEngine : public MediaEngine {
+ public:
+ GipsLiteMediaEngine();
+ ~GipsLiteMediaEngine() {}
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual int GetInputLevel() {return gips_.GIPSVE_GetInputLevel();}
+ virtual bool FindCodec(const Codec &codec);
+ virtual std::vector<Codec> codecs();
+ bool FindGIPSCodec(Codec codec, GIPS_CodecInst* gips_codec);
+ GipsVoiceEngineLite &gips() {return gips_;};
+
+ private:
+ GipsVoiceEngineLite & gips_;
+ int GetGIPSCodecPreference(const char *name, int clockrate);
+ std::vector<Codec> codecs_;
+};
+
+class GipsLiteMediaChannel : public MediaChannel, public GIPS_transport {
+ public:
+ GipsLiteMediaChannel(GipsLiteMediaEngine *me);
+ virtual ~GipsLiteMediaChannel() {
+ StopMediaMonitor();
+ engine_->gips().GIPSVE_DeleteChannel(gips_channel_);
+ }
+ virtual void SetCodecs(const std::vector<Codec> &codecs);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+
+ virtual int GetOutputLevel() {return engine_->gips().GIPSVE_GetOutputLevel(gips_channel_);}
+ GipsLiteMediaEngine *engine();
+
+ virtual void StartMediaMonitor(VoiceChannel *voice_channel, uint32 cms) {}
+ virtual void StopMediaMonitor() {}
+
+ private:
+ GipsLiteMediaEngine *engine_;
+ int gips_channel_;
+
+ virtual int SendPacket(int channel, const void *data, int len) {if (network_interface_) network_interface_->SendPacket(data, len); return 1;}
+ virtual int SendRTCPPacket(int channel, const void *data, int len) {return 1;}
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_GIPSLITEMEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
new file mode 100644
index 0000000..436e16e
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
@@ -0,0 +1,207 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+#include <mediastreamer2/mediastream.h>
+//#ifdef HAVE_ILBC
+//#include <mediastreamer2/msilbcdec.h>
+//#endif
+//#ifdef HAVE_SPEEX
+//#include <mediastreamer2/msspeexdec.h>
+//#endif
+
+#include <ortp/ortp.h>
+#include <ortp/telephonyevents.h>
+//#include <netdb.h>
+//#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/base/helpers.h"
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/linphonemediaengine.h"
+
+using namespace cricket;
+
+void LinphoneMediaChannel::OnIncomingData(talk_base::AsyncSocket *s)
+{
+ char *buf[2048];
+ int len;
+ len = s->Recv(buf, sizeof(buf));
+ if (network_interface_ && !mute_)
+ network_interface_->SendPacket(buf, len);
+}
+
+LinphoneMediaChannel::LinphoneMediaChannel(LinphoneMediaEngine*eng) :
+ pt_(-1),
+ audio_stream_(0),
+ engine_(eng) {
+
+ talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread();
+ talk_base::SocketServer *ss = thread->socketserver();
+ socket_.reset(ss->CreateAsyncSocket(SOCK_DGRAM));
+
+ port_in_ = 2000 + CreateRandomId() % 1000;
+ port_out_ = 3000 + CreateRandomId() % 1000;
+
+ socket_->Bind(talk_base::SocketAddress("localhost", port_out_));
+ socket_->SignalReadEvent.connect(this, &LinphoneMediaChannel::OnIncomingData);
+}
+
+LinphoneMediaChannel::~LinphoneMediaChannel() {
+ if (audio_stream_ != NULL)
+ audio_stream_stop(audio_stream_);
+}
+
+void LinphoneMediaChannel::SetCodecs(const std::vector<Codec> &codecs) {
+ bool first = true;
+ std::vector<Codec>::const_iterator i;
+
+ for (i = codecs.begin(); i < codecs.end(); i++) {
+
+ if (!engine_->FindCodec(*i))
+ continue;
+#ifdef HAVE_ILBC
+ if (i->name == payload_type_ilbc.mime_type) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_ilbc);
+ }
+#endif
+#ifdef HAVE_SPEEX
+ if (i->name == payload_type_speex_wb.mime_type && i->clockrate == payload_type_speex_wb.clock_rate) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_wb);
+ } else if (i->name == payload_type_speex_nb.mime_type && i->clockrate == payload_type_speex_nb.clock_rate) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_speex_nb);
+ }
+#endif
+
+ if (i->id == 0)
+ rtp_profile_set_payload(&av_profile, 0, &payload_type_pcmu8000);
+
+ if (i->name == payload_type_telephone_event.mime_type) {
+ rtp_profile_set_payload(&av_profile, i->id, &payload_type_telephone_event);
+ }
+
+ if (first) {
+ LOG(LS_INFO) << "Using " << i->name << "/" << i->clockrate;
+ pt_ = i->id;
+ audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, i->id, 250, false);
+ first = false;
+ }
+ }
+
+ if (first) {
+ // We're being asked to set an empty list of codecs. This will only happen when
+ // working with a buggy client; let's try PCMU.
+ LOG(LS_WARNING) << "Received empty list of codces; using PCMU/8000";
+ audio_stream_ = audio_stream_start(&av_profile, port_in_, "127.0.0.1", port_out_, 0, 250, false);
+ }
+
+}
+
+bool LinphoneMediaEngine::FindCodec(const Codec &c) {
+ if (c.id == 0)
+ return true;
+ if (c.name == payload_type_telephone_event.mime_type)
+ return true;
+#ifdef HAVE_SPEEX
+ if (c.name == payload_type_speex_wb.mime_type && c.clockrate == payload_type_speex_wb.clock_rate)
+ return true;
+ if (c.name == payload_type_speex_nb.mime_type && c.clockrate == payload_type_speex_nb.clock_rate)
+ return true;
+#endif
+#ifdef HAVE_ILBC
+ if (c.name == payload_type_ilbc.mime_type)
+ return true;
+#endif
+return false;
+}
+
+void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) {
+ uint8 buf[2048];
+ memcpy(buf, data, len);
+
+ /* We may receive packets with payload type 13: comfort noise. Linphone can't
+ * handle them, so let's ignore those packets.
+ */
+ int payloadtype = buf[1] & 0x7f;
+ if (play_ && payloadtype != 13)
+ socket_->SendTo(buf, len, talk_base::SocketAddress("localhost", port_in_));
+}
+
+void LinphoneMediaChannel::SetPlayout(bool playout) {
+ play_ = playout;
+}
+
+void LinphoneMediaChannel::SetSend(bool send) {
+ mute_ = !send;
+}
+
+int LinphoneMediaChannel::GetOutputLevel() { return 0; }
+
+LinphoneMediaEngine::LinphoneMediaEngine() {}
+LinphoneMediaEngine::~LinphoneMediaEngine() {}
+
+/*
+static void null_log_handler(const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data) {
+ LOG(LS_INFO) << log_domain << " " << message;
+}
+*/
+
+bool LinphoneMediaEngine::Init() {
+// g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, this);
+// g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, this);
+// g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, this);
+ ortp_init();
+ ms_init();
+
+#ifdef HAVE_SPEEX
+ //ms_speex_codec_init();
+
+ codecs_.push_back(Codec(110, payload_type_speex_wb.mime_type, payload_type_speex_wb.clock_rate, 0, 1, 8));
+ codecs_.push_back(Codec(111, payload_type_speex_nb.mime_type, payload_type_speex_nb.clock_rate, 0, 1, 7));
+
+#endif
+
+#ifdef HAVE_ILBC
+ //ms_ilbc_codec_init();
+ codecs_.push_back(Codec(102, payload_type_ilbc.mime_type, payload_type_ilbc.clock_rate, 0, 1, 4));
+#endif
+
+ codecs_.push_back(Codec(0, payload_type_pcmu8000.mime_type, payload_type_pcmu8000.clock_rate, 0, 1, 2));
+ codecs_.push_back(Codec(101, payload_type_telephone_event.mime_type, payload_type_telephone_event.clock_rate, 0, 1, 1));
+ return true;
+}
+
+void LinphoneMediaEngine::Terminate() {
+
+}
+
+MediaChannel *LinphoneMediaEngine::CreateChannel() {
+ return new LinphoneMediaChannel(this);
+}
+
+int LinphoneMediaEngine::SetAudioOptions(int options) { return 0; }
+int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) { return 0; }
+
+float LinphoneMediaEngine::GetCurrentQuality() { return 0; }
+int LinphoneMediaEngine::GetInputLevel() { return 0; }
diff --git a/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h
new file mode 100644
index 0000000..8e57d43
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/linphonemediaengine.h
@@ -0,0 +1,88 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+
+#include <mediastreamer2/mediastream.h>
+#include "talk/base/asyncsocket.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class LinphoneMediaEngine;
+
+class LinphoneMediaChannel : public MediaChannel {
+ public:
+ LinphoneMediaChannel(LinphoneMediaEngine *eng);
+ virtual ~LinphoneMediaChannel();
+
+ virtual void SetCodecs(const std::vector<Codec> &codecs);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+
+ virtual int GetOutputLevel();
+ bool mute() {return mute_;}
+
+ virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) {}
+ virtual void StopMediaMonitor() {}
+
+ private:
+ LinphoneMediaEngine *engine_;
+ AudioStream *audio_stream_;
+ talk_base::scoped_ptr<talk_base::AsyncSocket> socket_;
+ void OnIncomingData(talk_base::AsyncSocket *s);
+ int pt_;
+ bool mute_;
+ bool play_;
+ int port_in_;
+ int port_out_;
+};
+
+class LinphoneMediaEngine : public MediaEngine {
+ public:
+ LinphoneMediaEngine();
+ ~LinphoneMediaEngine();
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual float GetCurrentQuality();
+ virtual int GetInputLevel();
+
+ virtual std::vector<Codec, std::allocator<Codec> > codecs() {return codecs_;}
+ virtual bool FindCodec(const Codec&);
+
+ private:
+ std::vector<Codec, std::allocator<Codec> > codecs_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h
new file mode 100644
index 0000000..65463a5
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/mediachannel.h
@@ -0,0 +1,78 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_
+#define TALK_SESSION_PHONE_MEDIACHANNEL_H_
+
+#include "talk/base/basictypes.h"
+#include "talk/base/sigslot.h"
+#include "talk/session/phone/codec.h"
+
+namespace cricket {
+
+class VoiceChannel;
+
+struct MediaInfo {
+ unsigned short fraction_lost;
+ unsigned long cum_lost;
+ unsigned long ext_max;
+ unsigned long jitter;
+ int RTT;
+ int bytesSent;
+ int packetsSent;
+ int bytesReceived;
+ int packetsReceived;
+};
+
+class MediaChannel : public sigslot::has_slots<> {
+ public:
+ class NetworkInterface {
+ public:
+ virtual void SendPacket(const void *data, size_t len) = 0;
+ };
+ MediaChannel() {network_interface_ = NULL;}
+ virtual ~MediaChannel() {};
+ void SetInterface(NetworkInterface *iface) {network_interface_ = iface;}
+ virtual void SetCodecs(const std::vector<Codec> &codecs) = 0;
+ virtual void OnPacketReceived(const void *data, int len) = 0;
+ virtual void SetPlayout(bool playout) = 0;
+ virtual void SetSend(bool send) = 0;
+ virtual int GetOutputLevel() = 0;
+
+ virtual void StartMediaMonitor(VoiceChannel * voice_channel, uint32 cms) = 0;
+ virtual void StopMediaMonitor() = 0;
+ sigslot::signal2<MediaChannel *, const MediaInfo &> SignalMediaMonitor;
+
+
+ NetworkInterface *network_interface() {return network_interface_;}
+ protected:
+ NetworkInterface *network_interface_;
+};
+
+}; // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h
new file mode 100644
index 0000000..e8081db
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/mediaengine.h
@@ -0,0 +1,70 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+// MediaEngine is an abstraction of a media engine which can be subclassed
+// to support different media componentry backends.
+
+#ifndef TALK_SESSION_PHONE_MEDIAENGINE_H_
+#define TALK_SESSION_PHONE_MEDIAENGINE_H_
+
+#include <string>
+#include <vector>
+
+#include "talk/session/phone/codec.h"
+#include "talk/session/phone/mediachannel.h"
+
+namespace cricket {
+
+class MediaEngine {
+ public:
+
+ MediaEngine() {}
+
+ // Bitmask flags for options that may be supported by the media engine implementation
+ enum MediaEngineOptions {
+ AUTO_GAIN_CONTROL = 1 << 1,
+ };
+
+ // Initialize
+ virtual bool Init() = 0;
+ virtual void Terminate() = 0;
+ virtual MediaChannel *CreateChannel() = 0;
+
+ virtual int SetAudioOptions(int options) = 0;
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device) = 0;
+ virtual int GetInputLevel() = 0;
+
+ virtual std::vector<Codec> codecs() = 0;
+
+ virtual bool FindCodec(const Codec &codec) = 0;
+
+ int GetCodecPreference (Codec codec);
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc
new file mode 100644
index 0000000..60daf18
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.cc
@@ -0,0 +1,278 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/base/logging.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/qname.h"
+namespace {
+
+const std::string NS_PHONE("http://www.google.com/session/phone");
+const std::string NS_EMPTY("");
+
+const buzz::QName QN_PHONE_DESCRIPTION(true, NS_PHONE, "description");
+const buzz::QName QN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type");
+const buzz::QName QN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id");
+const buzz::QName QN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name");
+const buzz::QName QN_PHONE_PAYLOADTYPE_RATE(true, NS_EMPTY, "clockrate");
+const buzz::QName QN_PHONE_PAYLOADTYPE_BITRATE(true, NS_EMPTY, "bitrate");
+const buzz::QName QN_PHONE_PAYLOADTYPE_CHANNELS(true, NS_EMPTY, "channels");
+}
+
+namespace cricket {
+
+PhoneSessionClient::PhoneSessionClient(
+ const buzz::Jid& jid, SessionManager *manager)
+ : jid_(jid), session_manager_(manager) {
+ // No call to start, and certainly no call with focus
+ focus_call_ = NULL;
+
+ // Start up the channel manager on a worker thread
+ channel_manager_ = new ChannelManager(session_manager_->worker_thread());
+
+ // Register ourselves as the handler of phone sessions.
+ session_manager_->AddClient(NS_PHONE, this);
+}
+
+
+PhoneSessionClient::~PhoneSessionClient() {
+ // Destroy all calls
+ std::map<uint32, Call *>::iterator it;
+ while (calls_.begin() != calls_.end()) {
+ std::map<uint32, Call *>::iterator it = calls_.begin();
+ DestroyCall((*it).second);
+ }
+
+ // Delete channel manager. This will wait for the channels to exit
+ delete channel_manager_;
+
+ // Remove ourselves from the client map.
+ session_manager_->RemoveClient(NS_PHONE);
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() {
+ PhoneSessionDescription* session_desc = new PhoneSessionDescription();
+
+
+ MediaEngine *me = channel_manager_->media_engine();
+ std::vector<Codec> codecs = me->codecs();
+ std::vector<Codec>::iterator i;
+ for (i = codecs.begin(); i < codecs.end(); i++)
+ session_desc->AddCodec(*i);
+
+ session_desc->Sort();
+ return session_desc;
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) {
+ const PhoneSessionDescription* offer_desc =
+ static_cast<const PhoneSessionDescription*>(offer);
+ PhoneSessionDescription* accept_desc = new PhoneSessionDescription();
+ std::vector<Codec> codecs = channel_manager_->media_engine()->codecs();
+ std::vector<Codec>::iterator iter;
+ for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) {
+ if (channel_manager_->media_engine()->FindCodec(offer_desc->codecs()[i]))
+ accept_desc->AddCodec(offer_desc->codecs()[i]);
+ }
+
+ accept_desc->Sort();
+ return accept_desc;
+}
+
+const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) {
+ PhoneSessionDescription* desc = new PhoneSessionDescription();
+
+ const buzz::XmlElement* payload_type = element->FirstNamed(QN_PHONE_PAYLOADTYPE);
+ int num_payload_types = 0;
+
+ while (payload_type) {
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_ID)) {
+ int id = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_ID).c_str());
+
+ std::string name = "";
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_NAME))
+ name = payload_type->Attr(QN_PHONE_PAYLOADTYPE_NAME);
+
+ int clockrate = 0;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_RATE))
+ clockrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_RATE).c_str());
+
+ int bitrate = 0;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_BITRATE))
+ bitrate = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_BITRATE).c_str());
+
+ int channels = 1;
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_CHANNELS))
+ channels = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_CHANNELS).c_str());
+
+ desc->AddCodec(Codec(id, name, clockrate, bitrate, channels, 0));
+ }
+
+ payload_type = payload_type->NextNamed(QN_PHONE_PAYLOADTYPE);
+ num_payload_types += 1;
+ }
+
+ // For backward compatability, we can assume the other client is (an old
+ // version of Talk) if it has no payload types at all.
+ if (num_payload_types == 0) {
+ desc->AddCodec(Codec(103, "ISAC", 16000, -1, 1, 1));
+ desc->AddCodec(Codec(0, "PCMU", 8000, 64000, 1, 0));
+ }
+
+ return desc;
+}
+
+buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) {
+ const PhoneSessionDescription* session_desc =
+ static_cast<const PhoneSessionDescription*>(_session_desc);
+ buzz::XmlElement* description = new buzz::XmlElement(QN_PHONE_DESCRIPTION, true);
+
+
+ for (size_t i = 0; i < session_desc->codecs().size(); ++i) {
+ buzz::XmlElement* payload_type = new buzz::XmlElement(QN_PHONE_PAYLOADTYPE, true);
+
+ char buf[32];
+ sprintf(buf, "%d", session_desc->codecs()[i].id);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_ID, buf);
+
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_NAME,
+ session_desc->codecs()[i].name.c_str());
+
+ if (session_desc->codecs()[i].clockrate > 0) {
+ sprintf(buf, "%d", session_desc->codecs()[i].clockrate);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_RATE, buf);
+ }
+
+ if (session_desc->codecs()[i].channels > 1) {
+ sprintf(buf, "%d", session_desc->codecs()[i].channels);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_CHANNELS, buf);
+ }
+
+ if (session_desc->codecs()[i].bitrate > 0) {
+ sprintf(buf, "%d", session_desc->codecs()[i].bitrate);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_BITRATE, buf);
+ }
+
+ description->AddElement(payload_type);
+ }
+
+ return description;
+}
+
+Call *PhoneSessionClient::CreateCall() {
+ Call *call = new Call(this);
+ calls_[call->id()] = call;
+ SignalCallCreate(call);
+ return call;
+}
+
+void PhoneSessionClient::OnSessionCreate(Session *session,
+ bool received_initiate) {
+ if (received_initiate) {
+ session->SignalState.connect(this, &PhoneSessionClient::OnSessionState);
+
+ Call *call = CreateCall();
+ session_map_[session->id()] = call;
+ call->AddSession(session);
+ }
+}
+
+void PhoneSessionClient::OnSessionState(Session *session,
+ Session::State state) {
+ if (state == Session::STATE_RECEIVEDINITIATE) {
+ // If our accept would have no codecs, then we must reject this call.
+ PhoneSessionDescription* accept_desc =
+ CreateAcceptSessionDescription(session->remote_description());
+ if (accept_desc->codecs().size() == 0) {
+ // TODO: include an error description with the rejection.
+ session->Reject();
+ }
+ delete accept_desc;
+ }
+}
+
+void PhoneSessionClient::DestroyCall(Call *call) {
+ // Change focus away, signal destruction
+
+ if (call == focus_call_)
+ SetFocus(NULL);
+ SignalCallDestroy(call);
+
+ // Remove it from calls_ map and delete
+
+ std::map<uint32, Call *>::iterator it = calls_.find(call->id());
+ if (it != calls_.end())
+ calls_.erase(it);
+
+ delete call;
+}
+
+void PhoneSessionClient::OnSessionDestroy(Session *session) {
+ // Find the call this session is in, remove it
+
+ std::map<SessionID, Call *>::iterator it = session_map_.find(session->id());
+ assert(it != session_map_.end());
+ if (it != session_map_.end()) {
+ Call *call = (*it).second;
+ session_map_.erase(it);
+ call->RemoveSession(session);
+ }
+}
+
+Call *PhoneSessionClient::GetFocus() {
+ return focus_call_;
+}
+
+void PhoneSessionClient::SetFocus(Call *call) {
+ Call *old_focus_call = focus_call_;
+ if (focus_call_ != call) {
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(false);
+ focus_call_ = call;
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(true);
+ SignalFocus(focus_call_, old_focus_call);
+ }
+}
+
+void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) {
+ // Move all sessions from call to call_to_join, delete call.
+ // If call_to_join has focus, added sessions should have enabled channels.
+
+ if (focus_call_ == call)
+ SetFocus(NULL);
+ call_to_join->Join(call, focus_call_ == call_to_join);
+ DestroyCall(call);
+}
+
+Session *PhoneSessionClient::CreateSession(Call *call) {
+ Session *session = session_manager_->CreateSession(jid().Str(), NS_PHONE);
+ session_map_[session->id()] = call;
+ return session;
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h
new file mode 100644
index 0000000..35083c5
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/phonesessionclient.h
@@ -0,0 +1,132 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _PHONESESSIONCLIENT_H_
+#define _PHONESESSIONCLIENT_H_
+
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include <map>
+
+namespace cricket {
+
+class Call;
+class PhoneSessionDescription;
+
+class PhoneSessionClient: public SessionClient, public sigslot::has_slots<> {
+public:
+ PhoneSessionClient(const buzz::Jid& jid, SessionManager *manager);
+ ~PhoneSessionClient();
+
+ const buzz::Jid &jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+ ChannelManager* channel_manager() const { return channel_manager_; }
+
+ Call *CreateCall();
+ void DestroyCall(Call *call);
+
+ Call *GetFocus();
+ void SetFocus(Call *call);
+
+ Call *GetCall(uint32 id) {
+ std::map<uint32, Call *>::iterator it = calls_.find(id);
+ if (it != calls_.end())
+ return it->second;
+ return NULL;
+ }
+
+ void JoinCalls(Call *call_to_join, Call *call);
+
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ if (channel_manager_)
+ channel_manager_->SetAudioOptions(auto_gain_control, wave_in_device,
+ wave_out_device);
+ }
+
+ sigslot::signal2<Call *, Call *> SignalFocus;
+ sigslot::signal1<Call *> SignalCallCreate;
+ sigslot::signal1<Call *> SignalCallDestroy;
+
+ PhoneSessionDescription* CreateOfferSessionDescription();
+
+ PhoneSessionDescription* CreateAcceptSessionDescription(const SessionDescription* offer);
+
+ // Returns our preference for the given codec.
+ static int GetMediaCodecPreference(Codec codec);
+
+private:
+ void OnSessionCreate(Session *session, bool received_initiate);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionDestroy(Session *session);
+ const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element);
+ buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description);
+ Session *CreateSession(Call *call);
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ Call *focus_call_;
+ ChannelManager *channel_manager_;
+ std::map<uint32, Call *> calls_;
+ std::map<SessionID, Call *> session_map_;
+
+ friend class Call;
+};
+
+class PhoneSessionDescription: public SessionDescription {
+public:
+ // Returns the list of codecs sorted by our preference.
+ const std::vector<Codec>& codecs() const { return codecs_; }
+ // Adds another codec to the list.
+ void AddCodec(const Codec& codec) { codecs_.push_back(codec); }
+ // Sorts the list of codecs by preference.
+ void Sort() { std::sort(codecs_.begin(), codecs_.end(), PreferenceSort()); }
+
+private:
+ std::vector<Codec> codecs_;
+ struct PreferenceSort {
+ bool operator()(Codec a, Codec b) {
+ return a.preference > b.preference;
+ }
+ };
+
+#if defined(FEATURE_ENABLE_VOICEMAIL)
+ std::string lang_;
+#endif
+
+};
+
+}
+
+#endif // _PHONESESSIONCLIENT_H_
diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc
new file mode 100644
index 0000000..58b8760
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.cc
@@ -0,0 +1,330 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/session/phone/voicechannel.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include <cassert>
+#undef SetPort
+
+namespace cricket {
+
+
+VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session,
+ MediaChannel *channel) {
+ channel_manager_ = manager;
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ media_channel_ = channel;
+ session_ = session;
+ socket_monitor_ = NULL;
+ audio_monitor_ = NULL;
+ transport_channel_ = session_->CreateChannel("rtp");
+ transport_channel_->SignalWritableState.connect(
+ this, &VoiceChannel::OnWritableState);
+ transport_channel_->SignalReadPacket.connect(
+ this, &VoiceChannel::OnChannelRead);
+ media_channel_->SetInterface(this);
+ enabled_ = false;
+ paused_ = false;
+ writable_ = false;
+ muted_ = false;
+ LOG(INFO) << "Created voice channel";
+
+ session->SignalState.connect(this, &VoiceChannel::OnSessionState);
+ OnSessionState(session, session->state());
+}
+
+VoiceChannel::~VoiceChannel() {
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ enabled_ = false;
+ ChangeState();
+ delete socket_monitor_;
+ delete audio_monitor_;
+ talk_base::Thread::Current()->Clear(this);
+ if (transport_channel_ != NULL)
+ session_->DestroyChannel(transport_channel_);
+ LOG(INFO) << "Destroyed voice channel";
+}
+
+void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_ENABLE:
+ EnableMedia_w();
+ break;
+
+ case MSG_DISABLE:
+ DisableMedia_w();
+ break;
+
+ case MSG_MUTE:
+ MuteMedia_w();
+ break;
+
+ case MSG_UNMUTE:
+ UnmuteMedia_w();
+ break;
+
+ case MSG_SETSENDCODEC:
+ SetSendCodec_w();
+ break;
+ }
+}
+
+void VoiceChannel::Enable(bool enable) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this,
+ enable ? MSG_ENABLE : MSG_DISABLE);
+}
+
+void VoiceChannel::Mute(bool mute) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE);
+}
+
+
+MediaChannel * VoiceChannel::channel() {
+ return media_channel_;
+}
+
+void VoiceChannel::OnSessionState(Session* session, Session::State state) {
+ if ((state == Session::STATE_RECEIVEDACCEPT) ||
+ (state == Session::STATE_RECEIVEDINITIATE)) {
+ channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC);
+ }
+}
+
+void VoiceChannel::SetSendCodec_w() {
+ assert(channel_manager_->worker_thread() == talk_base::Thread::Current());
+
+ const PhoneSessionDescription* desc =
+ static_cast<const PhoneSessionDescription*>(
+ session()->remote_description());
+
+ media_channel_->SetCodecs(desc->codecs());
+}
+
+void VoiceChannel::OnWritableState(TransportChannel* channel) {
+ ASSERT(channel == transport_channel_);
+ if (transport_channel_->writable()) {
+ ChannelWritable_w();
+ } else {
+ ChannelNotWritable_w();
+ }
+}
+
+void VoiceChannel::OnChannelRead(TransportChannel* channel,
+ const char* data,
+ size_t len) {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
+ media_channel_->OnPacketReceived(data, (int)len);
+}
+
+void VoiceChannel::SendPacket(const void *data, size_t len) {
+ // SendPacket gets called from MediaEngine; send to socket
+ // MediaEngine will call us on a random thread. The Send operation on the
+ // socket is special in that it can handle this.
+ transport_channel_->SendPacket(static_cast<const char *>(data), len);
+}
+
+void VoiceChannel::ChangeState() {
+ if (paused_ || !enabled_ || !writable_) {
+ media_channel_->SetPlayout(false);
+ media_channel_->SetSend(false);
+ } else {
+ if (muted_) {
+ media_channel_->SetSend(false);
+ media_channel_->SetPlayout(true);
+ } else {
+ media_channel_->SetSend(true);
+ media_channel_->SetPlayout(true);
+ }
+ }
+}
+
+void VoiceChannel::PauseMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ ASSERT(!paused_);
+
+ LOG(INFO) << "Voice channel paused";
+ paused_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnpauseMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ ASSERT(paused_);
+
+ LOG(INFO) << "Voice channel unpaused";
+ paused_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::EnableMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel enabled";
+ enabled_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::DisableMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel disabled";
+ enabled_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::MuteMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (muted_)
+ return;
+
+ LOG(INFO) << "Voice channel muted";
+ muted_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnmuteMedia_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!muted_)
+ return;
+
+ LOG(INFO) << "Voice channel unmuted";
+ muted_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::ChannelWritable_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket writable";
+ writable_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::ChannelNotWritable_w() {
+ ASSERT(channel_manager_->worker_thread() == talk_base::Thread::Current());
+ if (!writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket not writable";
+ writable_ = false;
+ ChangeState();
+}
+
+
+void VoiceChannel::StartConnectionMonitor(int cms) {
+ delete socket_monitor_;
+ socket_monitor_ =
+ new SocketMonitor(session_, transport_channel_,
+ talk_base::Thread::Current());
+ socket_monitor_->SignalUpdate.connect(
+ this, &VoiceChannel::OnConnectionMonitorUpdate);
+ socket_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopConnectionMonitor() {
+ if (socket_monitor_ != NULL) {
+ socket_monitor_->Stop();
+ socket_monitor_->SignalUpdate.disconnect(this);
+ delete socket_monitor_;
+ socket_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnConnectionMonitorUpdate(
+ SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void VoiceChannel::StartAudioMonitor(int cms) {
+ delete audio_monitor_;
+ audio_monitor_ = new AudioMonitor(this, talk_base::Thread::Current());
+ audio_monitor_
+ ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate);
+ audio_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopAudioMonitor() {
+ if (audio_monitor_ != NULL) {
+ audio_monitor_ ->Stop();
+ audio_monitor_ ->SignalUpdate.disconnect(this);
+ delete audio_monitor_ ;
+ audio_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor,
+ const AudioInfo& info) {
+ SignalAudioMonitor(this, info);
+}
+
+void VoiceChannel::StartMediaMonitor(int cms) {
+ media_channel_
+ ->SignalMediaMonitor.connect(this, &VoiceChannel::OnMediaMonitorUpdate);
+ media_channel_->StartMediaMonitor(this, cms);
+}
+
+void VoiceChannel::StopMediaMonitor() {
+ media_channel_->SignalMediaMonitor.disconnect(this);
+ media_channel_->StopMediaMonitor();
+}
+
+void VoiceChannel::OnMediaMonitorUpdate(
+ MediaChannel *media_channel, const MediaInfo &info) {
+ ASSERT(media_channel == media_channel_);
+ SignalMediaMonitor(this, info);
+}
+
+Session *VoiceChannel::session() {
+ return session_;
+}
+
+int VoiceChannel::GetInputLevel_w() {
+ return channel_manager_->media_engine()->GetInputLevel();
+}
+
+int VoiceChannel::GetOutputLevel_w() {
+ return media_channel_->GetOutputLevel();
+}
+
+talk_base::Thread* VoiceChannel::worker_thread() {
+ return channel_manager_->worker_thread();
+}
+
+}
diff --git a/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h
new file mode 100644
index 0000000..3d0be12
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/phone/voicechannel.h
@@ -0,0 +1,137 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef _VOICECHANNEL_H_
+#define _VOICECHANNEL_H_
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/network.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/mediachannel.h"
+
+namespace cricket {
+
+const uint32 MSG_ENABLE = 1;
+const uint32 MSG_DISABLE = 2;
+const uint32 MSG_MUTE = 3;
+const uint32 MSG_UNMUTE = 4;
+const uint32 MSG_SETSENDCODEC = 5;
+
+
+class ChannelManager;
+
+class VoiceChannel
+ : public talk_base::MessageHandler, public sigslot::has_slots<>,
+ public MediaChannel::NetworkInterface {
+ public:
+ VoiceChannel(ChannelManager *manager, Session *session,
+ MediaChannel *channel);
+ ~VoiceChannel();
+
+ void Enable(bool enable);
+ void Mute(bool mute);
+
+
+ MediaChannel *channel();
+ Session *session();
+
+ // Monitoring
+
+ void StartConnectionMonitor(int cms);
+ void StopConnectionMonitor();
+ sigslot::signal2<VoiceChannel *, const std::vector<ConnectionInfo> &>
+ SignalConnectionMonitor;
+
+ void StartAudioMonitor(int cms);
+ void StopAudioMonitor();
+ sigslot::signal2<VoiceChannel *, const AudioInfo&> SignalAudioMonitor;
+ talk_base::Thread* worker_thread();
+
+ void StartMediaMonitor(int cms);
+ void StopMediaMonitor();
+ sigslot::signal2<VoiceChannel *, const MediaInfo&> SignalMediaMonitor;
+
+ // Pausing so that the ChannelManager can change the audio devices. These
+ // should only be called from the worker thread
+ void PauseMedia_w();
+ void UnpauseMedia_w();
+
+ int GetInputLevel_w();
+ int GetOutputLevel_w();
+
+ // MediaEngine calls this
+ virtual void SendPacket(const void *data, size_t len);
+
+private:
+ void ChangeState();
+ void EnableMedia_w();
+ void DisableMedia_w();
+ void MuteMedia_w();
+ void UnmuteMedia_w();
+ void ChannelWritable_w();
+ void ChannelNotWritable_w();
+
+
+ void OnConnectionMonitorUpdate(SocketMonitor *monitor,
+ const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info);
+ void OnMediaMonitorUpdate(MediaChannel *media_channel,
+ const MediaInfo& info);
+
+ // From MessageHandler
+
+ void OnMessage(talk_base::Message *pmsg);
+
+ // Setting the send codec based on the remote description.
+ void OnSessionState(Session* session, Session::State state);
+ void SetSendCodec_w();
+
+ // From TransportChannel
+
+ void OnWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char *data, size_t len);
+
+
+ bool enabled_;
+ bool paused_;
+ bool writable_;
+ bool muted_;
+ MediaChannel *media_channel_;
+ Session *session_;
+ TransportChannel *transport_channel_;
+ ChannelManager *channel_manager_;
+ SocketMonitor *socket_monitor_;
+ AudioMonitor *audio_monitor_;
+};
+
+}
+
+#endif // _VOICECHANNEL_H_
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am
new file mode 100644
index 0000000..3a8d244
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.am
@@ -0,0 +1,7 @@
+libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \
+ pseudotcpchannel.cc
+noinst_HEADERS = tunnelsessionclient.h \
+ pseudotcpchannel.h
+noinst_LTLIBRARIES = libcricketsessiontunnel.la
+
+AM_CXXFLAGS = -DPOSIX
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in
new file mode 100644
index 0000000..4df78cc
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/Makefile.in
@@ -0,0 +1,452 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+subdir = talk/session/tunnel
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/talk/pkg.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libcricketsessiontunnel_la_LIBADD =
+am_libcricketsessiontunnel_la_OBJECTS = tunnelsessionclient.lo \
+ pseudotcpchannel.lo
+libcricketsessiontunnel_la_OBJECTS = \
+ $(am_libcricketsessiontunnel_la_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(libcricketsessiontunnel_la_SOURCES)
+DIST_SOURCES = $(libcricketsessiontunnel_la_SOURCES)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXPAT_LIBS = @EXPAT_LIBS@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GIPS_FALSE = @GIPS_FALSE@
+GIPS_TRUE = @GIPS_TRUE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LIBS = @GLIB_LIBS@
+ILBC_CFLAGS = @ILBC_CFLAGS@
+ILBC_LIBS = @ILBC_LIBS@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MEDIA_LIBS = @MEDIA_LIBS@
+OBJEXT = @OBJEXT@
+ORTP_CFLAGS = @ORTP_CFLAGS@
+ORTP_LIBS = @ORTP_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PHONE_FALSE = @PHONE_FALSE@
+PHONE_TRUE = @PHONE_TRUE@
+PKG_CONFIG = @PKG_CONFIG@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPEEX_CFLAGS = @SPEEX_CFLAGS@
+SPEEX_LIBS = @SPEEX_LIBS@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+ac_ct_RANLIB = @ac_ct_RANLIB@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+libcricketsessiontunnel_la_SOURCES = tunnelsessionclient.cc \
+ pseudotcpchannel.cc
+
+noinst_HEADERS = tunnelsessionclient.h \
+ pseudotcpchannel.h
+
+noinst_LTLIBRARIES = libcricketsessiontunnel.la
+AM_CXXFLAGS = -DPOSIX
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu talk/session/tunnel/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu talk/session/tunnel/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libcricketsessiontunnel.la: $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_DEPENDENCIES)
+ $(CXXLINK) $(libcricketsessiontunnel_la_LDFLAGS) $(libcricketsessiontunnel_la_OBJECTS) $(libcricketsessiontunnel_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pseudotcpchannel.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tunnelsessionclient.Plo@am__quote@
+
+.cc.o:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ if $(LTCXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc
new file mode 100644
index 0000000..54515c0
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.cc
@@ -0,0 +1,554 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "pseudotcpchannel.h"
+
+using namespace talk_base;
+
+namespace cricket {
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+// MSG_WK_* - worker thread messages
+// MSG_ST_* - stream thread messages
+// MSG_SI_* - signal thread messages
+
+enum {
+ MSG_WK_CLOCK = 1,
+ MSG_WK_PURGE,
+ MSG_ST_EVENT,
+ MSG_SI_DESTROYCHANNEL,
+ MSG_SI_DESTROY,
+};
+
+struct EventData : public MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel::InternalStream : public StreamInterface {
+public:
+ InternalStream(PseudoTcpChannel* parent);
+ virtual ~InternalStream();
+
+ virtual StreamState GetState() const;
+ virtual StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ virtual StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ virtual void Close();
+
+ virtual bool GetSize(size_t* size) const { return false; }
+ virtual bool ReserveSize(size_t size) { return true; }
+ virtual bool Rewind() { return false; }
+
+private:
+ // parent_ is accessed and modified exclusively on the event thread, to
+ // avoid thread contention. This means that the PseudoTcpChannel cannot go
+ // away until after it receives a Close() from TunnelStream.
+ PseudoTcpChannel* parent_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel
+// Member object lifetime summaries:
+// session_ - passed in constructor, cleared when channel_ goes away.
+// channel_ - created in Connect, destroyed when session_ or tcp_ goes away.
+// tcp_ - created in Connect, destroyed when channel_ goes away, or connection
+// closes.
+// worker_thread_ - created when channel_ is created, purged when channel_ is
+// destroyed.
+// stream_ - created in GetStream, destroyed by owner at arbitrary time.
+// this - created in constructor, destroyed when worker_thread_ and stream_
+// are both gone.
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signal thread methods
+//
+
+PseudoTcpChannel::PseudoTcpChannel(Thread* stream_thread, Session* session)
+ : signal_thread_(session->session_manager()->signaling_thread()),
+ worker_thread_(NULL),
+ stream_thread_(stream_thread),
+ session_(session), channel_(NULL), tcp_(NULL), stream_(NULL),
+ stream_readable_(false), pending_read_event_(false),
+ ready_to_connect_(false) {
+ ASSERT(signal_thread_->IsCurrent());
+}
+
+PseudoTcpChannel::~PseudoTcpChannel() {
+ ASSERT(signal_thread_->IsCurrent());
+ ASSERT(worker_thread_ == NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+ ASSERT(stream_ == NULL);
+ ASSERT(tcp_ == NULL);
+}
+
+bool PseudoTcpChannel::Connect(const std::string& channel_name) {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+
+ if (channel_)
+ return false;
+
+ ASSERT(session_ != NULL);
+ worker_thread_ = session_->session_manager()->worker_thread();
+ channel_ = session_->CreateChannel(channel_name);
+ channel_name_ = channel_name;
+ channel_->SetOption(Socket::OPT_DONTFRAGMENT, 1);
+
+ channel_->SignalDestroyed.connect(this,
+ &PseudoTcpChannel::OnChannelDestroyed);
+ channel_->SignalWritableState.connect(this,
+ &PseudoTcpChannel::OnChannelWritableState);
+ channel_->SignalReadPacket.connect(this,
+ &PseudoTcpChannel::OnChannelRead);
+ channel_->SignalRouteChange.connect(this,
+ &PseudoTcpChannel::OnChannelConnectionChanged);
+
+ ASSERT(tcp_ == NULL);
+ tcp_ = new PseudoTcp(this, 0);
+ if (session_->initiator()) {
+ // Since we may try several protocols and network adapters that won't work,
+ // waiting until we get our first writable notification before initiating
+ // TCP negotiation.
+ ready_to_connect_ = true;
+ }
+
+ return true;
+}
+
+StreamInterface* PseudoTcpChannel::GetStream() {
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(NULL != session_);
+ if (!stream_)
+ stream_ = new PseudoTcpChannel::InternalStream(this);
+ //TODO("should we disallow creation of new stream at some point?");
+ return stream_;
+}
+
+void PseudoTcpChannel::OnChannelDestroyed(TransportChannel* channel) {
+ LOG_F(LS_INFO) << "(" << channel->name() << ")";
+ ASSERT(signal_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ ASSERT(channel == channel_);
+ signal_thread_->Clear(this, MSG_SI_DESTROYCHANNEL);
+ // When MSG_WK_PURGE is received, we know there will be no more messages from
+ // the worker thread.
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->Post(this, MSG_WK_PURGE);
+ session_ = NULL;
+ channel_ = NULL;
+ if ((stream_ != NULL)
+ && ((tcp_ == NULL) || (tcp_->State() != PseudoTcp::TCP_CLOSED)))
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, 0));
+ if (tcp_) {
+ tcp_->Close(true);
+ AdjustClock();
+ }
+}
+
+//
+// Stream thread methods
+//
+
+StreamState PseudoTcpChannel::GetState() const {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SS_OPENING;
+ switch (tcp_->State()) {
+ case PseudoTcp::TCP_LISTEN:
+ case PseudoTcp::TCP_SYN_SENT:
+ case PseudoTcp::TCP_SYN_RECEIVED:
+ return SS_OPENING;
+ case PseudoTcp::TCP_ESTABLISHED:
+ return SS_OPEN;
+ case PseudoTcp::TCP_CLOSED:
+ default:
+ return SS_CLOSED;
+ }
+}
+
+StreamResult PseudoTcpChannel::Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+
+ stream_readable_ = false;
+ int result = tcp_->Recv(static_cast<char*>(buffer), buffer_len);
+ //LOG_F(LS_VERBOSE) << "Recv returned: " << result;
+ if (result > 0) {
+ if (read)
+ *read = result;
+ // PseudoTcp doesn't currently support repeated Readable signals. Simulate
+ // them here.
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ), true);
+ }
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ AdjustClock();
+}
+
+StreamResult PseudoTcpChannel::Write(const void* data, size_t data_len,
+ size_t* written, int* error) {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!tcp_)
+ return SR_BLOCK;
+ int result = tcp_->Send(static_cast<const char*>(data), data_len);
+ //LOG_F(LS_VERBOSE) << "Send returned: " << result;
+ if (result > 0) {
+ if (written)
+ *written = result;
+ return SR_SUCCESS;
+ } else if (IsBlockingError(tcp_->GetError())) {
+ return SR_BLOCK;
+ } else {
+ if (error)
+ *error = tcp_->GetError();
+ return SR_ERROR;
+ }
+ AdjustClock();
+}
+
+void PseudoTcpChannel::Close() {
+ ASSERT(stream_ != NULL && stream_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ stream_ = NULL;
+ // Clear out any pending event notifications
+ stream_thread_->Clear(this, MSG_ST_EVENT);
+ if (tcp_) {
+ tcp_->Close(false);
+ AdjustClock();
+ } else {
+ CheckDestroy();
+ }
+}
+
+//
+// Worker thread methods
+//
+
+void PseudoTcpChannel::OnChannelWritableState(TransportChannel* channel) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ if (!ready_to_connect_ || !channel->writable())
+ return;
+
+ ready_to_connect_ = false;
+ tcp_->Connect();
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelRead(TransportChannel* channel,
+ const char* data, size_t size) {
+ //LOG_F(LS_VERBOSE) << "(" << size << ")";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+ tcp_->NotifyPacket(data, size);
+ AdjustClock();
+}
+
+void PseudoTcpChannel::OnChannelConnectionChanged(TransportChannel* channel,
+ const SocketAddress& addr) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(worker_thread_->IsCurrent());
+ CritScope lock(&cs_);
+ if (!channel_) {
+ LOG_F(LS_WARNING) << "NULL channel";
+ return;
+ }
+ ASSERT(channel == channel_);
+ if (!tcp_) {
+ LOG_F(LS_WARNING) << "NULL tcp";
+ return;
+ }
+
+ scoped_ptr<Socket> mtu_socket(
+ worker_thread_->socketserver()
+ ->CreateSocket(SOCK_DGRAM));
+
+ uint16 mtu = 65535;
+ if (mtu_socket->Connect(addr) < 0) {
+ LOG_F(LS_ERROR) << "Socket::Connect: " << mtu_socket->GetError();
+ } else if (mtu_socket->EstimateMTU(&mtu) < 0) {
+ LOG_F(LS_ERROR) << "Socket::EstimateMTU: " << mtu_socket->GetError();
+ } else {
+ tcp_->NotifyMTU(mtu);
+ AdjustClock();
+ }
+}
+
+void PseudoTcpChannel::OnTcpOpen(PseudoTcp* tcp) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT,
+ new EventData(SE_OPEN | SE_READ | SE_WRITE));
+ }
+}
+
+void PseudoTcpChannel::OnTcpReadable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_) {
+ stream_readable_ = true;
+ if (!pending_read_event_) {
+ pending_read_event_ = true;
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_READ));
+ }
+ }
+}
+
+void PseudoTcpChannel::OnTcpWriteable(PseudoTcp* tcp) {
+ //LOG_F(LS_VERBOSE);
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_WRITE));
+}
+
+void PseudoTcpChannel::OnTcpClosed(PseudoTcp* tcp, uint32 nError) {
+ LOG_F(LS_VERBOSE) << "[" << channel_name_ << "]";
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(worker_thread_->IsCurrent());
+ ASSERT(tcp == tcp_);
+ if (stream_)
+ stream_thread_->Post(this, MSG_ST_EVENT, new EventData(SE_CLOSE, nError));
+}
+
+//
+// Multi-thread methods
+//
+
+void PseudoTcpChannel::OnMessage(Message* pmsg) {
+ if (pmsg->message_id == MSG_WK_CLOCK) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_WK_CLOCK)";
+ CritScope lock(&cs_);
+ if (tcp_) {
+ tcp_->NotifyClock(PseudoTcp::Now());
+ AdjustClock(false);
+ }
+
+ } else if (pmsg->message_id == MSG_WK_PURGE) {
+
+ ASSERT(worker_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_WK_PURGE)";
+ // At this point, we know there are no additional worker thread messages.
+ CritScope lock(&cs_);
+ ASSERT(NULL == session_);
+ ASSERT(NULL == channel_);
+ worker_thread_ = NULL;
+ CheckDestroy();
+
+ } else if (pmsg->message_id == MSG_ST_EVENT) {
+
+ ASSERT(stream_thread_->IsCurrent());
+ //LOG(LS_INFO) << "PseudoTcpChannel::OnMessage(MSG_ST_EVENT, "
+ // << data->event << ", " << data->error << ")";
+ ASSERT(stream_ != NULL);
+ EventData* data = static_cast<EventData*>(pmsg->pdata);
+ if (data->event & SE_READ) {
+ CritScope lock(&cs_);
+ pending_read_event_ = false;
+ }
+ stream_->SignalEvent(stream_, data->event, data->error);
+ delete data;
+
+ } else if (pmsg->message_id == MSG_SI_DESTROYCHANNEL) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROYCHANNEL)";
+ ASSERT(session_ != NULL);
+ ASSERT(channel_ != NULL);
+ session_->DestroyChannel(channel_);
+
+ } else if (pmsg->message_id == MSG_SI_DESTROY) {
+
+ ASSERT(signal_thread_->IsCurrent());
+ LOG_F(LS_INFO) << "(MSG_SI_DESTROY)";
+ // The message queue is empty, so it is safe to destroy ourselves.
+ delete this;
+
+ } else {
+ ASSERT(false);
+ }
+}
+
+IPseudoTcpNotify::WriteResult PseudoTcpChannel::TcpWritePacket(
+ PseudoTcp* tcp, const char* buffer, size_t len) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(tcp == tcp_);
+ ASSERT(NULL != channel_);
+ int sent = channel_->SendPacket(buffer, len);
+ if (sent > 0) {
+ //LOG_F(LS_VERBOSE) << "(" << sent << ") Sent";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (IsBlockingError(channel_->GetError())) {
+ LOG_F(LS_VERBOSE) << "Blocking";
+ return IPseudoTcpNotify::WR_SUCCESS;
+ } else if (channel_->GetError() == EMSGSIZE) {
+ LOG_F(LS_ERROR) << "EMSGSIZE";
+ return IPseudoTcpNotify::WR_TOO_LARGE;
+ } else {
+ PLOG(LS_ERROR, channel_->GetError()) << "PseudoTcpChannel::TcpWritePacket";
+ ASSERT(false);
+ return IPseudoTcpNotify::WR_FAIL;
+ }
+}
+
+void PseudoTcpChannel::AdjustClock(bool clear) {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ ASSERT(NULL != tcp_);
+
+ long timeout = 0;
+ if (tcp_->GetNextClock(PseudoTcp::Now(), timeout)) {
+ ASSERT(NULL != channel_);
+ // Reset the next clock, by clearing the old and setting a new one.
+ if (clear)
+ worker_thread_->Clear(this, MSG_WK_CLOCK);
+ worker_thread_->PostDelayed(_max(timeout, 0L), this, MSG_WK_CLOCK);
+ return;
+ }
+
+ delete tcp_;
+ tcp_ = NULL;
+ ready_to_connect_ = false;
+
+ if (channel_) {
+ // If TCP has failed, no need for channel_ anymore
+ signal_thread_->Post(this, MSG_SI_DESTROYCHANNEL);
+ }
+}
+
+void PseudoTcpChannel::CheckDestroy() {
+ ASSERT(cs_.CurrentThreadIsOwner());
+ if ((worker_thread_ != NULL) || (stream_ != NULL))
+ return;
+ signal_thread_->Post(this, MSG_SI_DESTROY);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PseudoTcpChannel::InternalStream
+///////////////////////////////////////////////////////////////////////////////
+
+PseudoTcpChannel::InternalStream::InternalStream(PseudoTcpChannel* parent)
+ : parent_(parent) {
+}
+
+PseudoTcpChannel::InternalStream::~InternalStream() {
+ Close();
+}
+
+StreamState PseudoTcpChannel::InternalStream::GetState() const {
+ if (!parent_)
+ return SS_CLOSED;
+ return parent_->GetState();
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Read(
+ void* buffer, size_t buffer_len, size_t* read, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Read(buffer, buffer_len, read, error);
+}
+
+StreamResult PseudoTcpChannel::InternalStream::Write(
+ const void* data, size_t data_len, size_t* written, int* error) {
+ if (!parent_) {
+ if (error)
+ *error = ENOTCONN;
+ return SR_ERROR;
+ }
+ return parent_->Write(data, data_len, written, error);
+}
+
+void PseudoTcpChannel::InternalStream::Close() {
+ if (!parent_)
+ return;
+ parent_->Close();
+ parent_ = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h
new file mode 100644
index 0000000..27574ea
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/pseudotcpchannel.h
@@ -0,0 +1,125 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef __PSEUDOTCPCHANNEL_H__
+#define __PSEUDOTCPCHANNEL_H__
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+
+namespace talk_base {
+class Thread;
+}
+
+namespace cricket {
+
+class TransportChannel;
+
+///////////////////////////////////////////////////////////////////////////////
+// ChannelStream
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel
+ : public IPseudoTcpNotify,
+ public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ // Signal thread methods
+ PseudoTcpChannel(talk_base::Thread* stream_thread,
+ Session* session);
+
+ bool Connect(const std::string& channel_name);
+ talk_base::StreamInterface* GetStream();
+
+ sigslot::signal1<PseudoTcpChannel*> SignalChannelClosed;
+
+private:
+ class InternalStream;
+ friend class InternalStream;
+
+ virtual ~PseudoTcpChannel();
+
+ // Stream thread methods
+ talk_base::StreamState GetState() const;
+ talk_base::StreamResult Read(void* buffer, size_t buffer_len,
+ size_t* read, int* error);
+ talk_base::StreamResult Write(const void* data, size_t data_len,
+ size_t* written, int* error);
+ void Close();
+
+ // Multi-thread methods
+ void OnMessage(talk_base::Message* pmsg);
+ void AdjustClock(bool clear = true);
+ void CheckDestroy();
+
+ // Signal thread methods
+ void OnChannelDestroyed(TransportChannel* channel);
+
+ // Worker thread methods
+ void OnChannelWritableState(TransportChannel* channel);
+ void OnChannelRead(TransportChannel* channel, const char* data, size_t size);
+ void OnChannelConnectionChanged(TransportChannel* channel,
+ const talk_base::SocketAddress& addr);
+
+ virtual void OnTcpOpen(PseudoTcp* ptcp);
+ virtual void OnTcpReadable(PseudoTcp* ptcp);
+ virtual void OnTcpWriteable(PseudoTcp* ptcp);
+ virtual void OnTcpClosed(PseudoTcp* ptcp, uint32 nError);
+ virtual IPseudoTcpNotify::WriteResult TcpWritePacket(PseudoTcp* tcp,
+ const char* buffer,
+ size_t len);
+
+ talk_base::Thread* signal_thread_, * worker_thread_, * stream_thread_;
+ Session* session_;
+ TransportChannel* channel_;
+ std::string channel_name_;
+ PseudoTcp* tcp_;
+ InternalStream* stream_;
+ bool stream_readable_, pending_read_event_;
+ bool ready_to_connect_;
+ mutable talk_base::CriticalSection cs_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __PSEUDOTCPCHANNEL_H__
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc
new file mode 100644
index 0000000..855f0db
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.cc
@@ -0,0 +1,316 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "talk/base/basicdefs.h"
+#include "talk/base/basictypes.h"
+#include "talk/base/common.h"
+#include "talk/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/base/stringutils.h"
+#include "talk/p2p/base/transportchannel.h"
+#include "talk/xmllite/xmlelement.h"
+#include "pseudotcpchannel.h"
+#include "tunnelsessionclient.h"
+
+namespace cricket {
+
+const std::string NS_TUNNEL("http://www.google.com/talk/tunnel");
+const buzz::QName QN_TUNNEL_DESCRIPTION(NS_TUNNEL, "description");
+const buzz::QName QN_TUNNEL_TYPE(NS_TUNNEL, "type");
+
+enum {
+ MSG_CLOCK = 1,
+ MSG_DESTROY,
+ MSG_TERMINATE,
+ MSG_EVENT,
+ MSG_CREATE_TUNNEL,
+};
+
+struct EventData : public talk_base::MessageData {
+ int event, error;
+ EventData(int ev, int err = 0) : event(ev), error(err) { }
+};
+
+struct CreateTunnelData : public talk_base::MessageData {
+ buzz::Jid jid;
+ std::string description;
+ talk_base::Thread* thread;
+ talk_base::StreamInterface* stream;
+};
+
+extern const talk_base::ConstantLabel SESSION_STATES[];
+
+const talk_base::ConstantLabel SESSION_STATES[] = {
+ KLABEL(Session::STATE_INIT),
+ KLABEL(Session::STATE_SENTINITIATE),
+ KLABEL(Session::STATE_RECEIVEDINITIATE),
+ KLABEL(Session::STATE_SENTACCEPT),
+ KLABEL(Session::STATE_RECEIVEDACCEPT),
+ KLABEL(Session::STATE_SENTMODIFY),
+ KLABEL(Session::STATE_RECEIVEDMODIFY),
+ KLABEL(Session::STATE_SENTREJECT),
+ KLABEL(Session::STATE_RECEIVEDREJECT),
+ KLABEL(Session::STATE_SENTREDIRECT),
+ KLABEL(Session::STATE_SENTTERMINATE),
+ KLABEL(Session::STATE_RECEIVEDTERMINATE),
+ KLABEL(Session::STATE_INPROGRESS),
+ KLABEL(Session::STATE_DEINIT),
+ LASTLABEL
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionDescription
+///////////////////////////////////////////////////////////////////////////////
+
+struct TunnelSessionDescription : public SessionDescription {
+ std::string description;
+
+ TunnelSessionDescription(const std::string& desc) : description(desc) { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+TunnelSessionClient::TunnelSessionClient(const buzz::Jid& jid,
+ SessionManager* manager)
+ : jid_(jid), session_manager_(manager), shutdown_(false) {
+ // Register ourselves as the handler of tunnel sessions.
+ session_manager_->AddClient(NS_TUNNEL, this);
+}
+
+TunnelSessionClient::~TunnelSessionClient() {
+ shutdown_ = true;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ Session* session = (*it)->ReleaseSession(true);
+ session_manager_->DestroySession(session);
+ }
+ session_manager_->RemoveClient(NS_TUNNEL);
+}
+
+const SessionDescription* TunnelSessionClient::CreateSessionDescription(
+ const buzz::XmlElement* element) {
+ if (const buzz::XmlElement* type_elem = element->FirstNamed(QN_TUNNEL_TYPE)) {
+ return new TunnelSessionDescription(type_elem->BodyText());
+ }
+ ASSERT(false);
+ return 0;
+}
+
+buzz::XmlElement* TunnelSessionClient::TranslateSessionDescription(
+ const SessionDescription* description) {
+ const TunnelSessionDescription* desc =
+ static_cast<const TunnelSessionDescription*>(description);
+
+ buzz::XmlElement* root = new buzz::XmlElement(QN_TUNNEL_DESCRIPTION, true);
+ buzz::XmlElement* type_elem = new buzz::XmlElement(QN_TUNNEL_TYPE);
+ type_elem->SetBodyText(desc->description);
+ root->AddElement(type_elem);
+ return root;
+}
+
+void TunnelSessionClient::OnSessionCreate(Session* session, bool received) {
+ LOG(LS_INFO) << "TunnelSessionClient::OnSessionCreate: received=" << received;
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (received)
+ sessions_.push_back(
+ new TunnelSession(this, session, talk_base::Thread::Current()));
+}
+
+void TunnelSessionClient::OnSessionDestroy(Session* session) {
+ LOG(LS_INFO) << "TunnelSessionClient::OnSessionDestroy";
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (shutdown_)
+ return;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ VERIFY((*it)->ReleaseSession(false) == session);
+ sessions_.erase(it);
+ return;
+ }
+ }
+}
+
+talk_base::StreamInterface* TunnelSessionClient::CreateTunnel(
+ const buzz::Jid& to, const std::string& description) {
+ // Valid from any thread
+ CreateTunnelData data;
+ data.jid = to;
+ data.description = description;
+ data.thread = talk_base::Thread::Current();
+ session_manager_->signaling_thread()->Send(this, MSG_CREATE_TUNNEL, &data);
+ return data.stream;
+}
+
+talk_base::StreamInterface* TunnelSessionClient::AcceptTunnel(
+ Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ TunnelSession* tunnel = NULL;
+ for (std::vector<TunnelSession*>::iterator it = sessions_.begin();
+ it != sessions_.end();
+ ++it) {
+ if ((*it)->HasSession(session)) {
+ tunnel = *it;
+ break;
+ }
+ }
+ ASSERT(tunnel != NULL);
+
+ const TunnelSessionDescription* in_desc =
+ static_cast<const TunnelSessionDescription*>(
+ session->remote_description());
+ TunnelSessionDescription* out_desc = new TunnelSessionDescription(
+ in_desc->description);
+ session->Accept(out_desc);
+ return tunnel->GetStream();
+}
+
+void TunnelSessionClient::DeclineTunnel(Session* session) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ session->Reject();
+}
+
+void TunnelSessionClient::OnMessage(talk_base::Message* pmsg) {
+ if (pmsg->message_id == MSG_CREATE_TUNNEL) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ CreateTunnelData* data = static_cast<CreateTunnelData*>(pmsg->pdata);
+ Session* session = session_manager_->CreateSession(jid_.Str(), NS_TUNNEL);
+ TunnelSession* tunnel = new TunnelSession(this, session, data->thread);
+ sessions_.push_back(tunnel);
+ TunnelSessionDescription* desc = new TunnelSessionDescription(data->description);
+ session->Initiate(data->jid.Str(), NULL, desc);
+ data->stream = tunnel->GetStream();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Signalling thread methods
+//
+
+TunnelSession::TunnelSession(TunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread)
+ : client_(client), session_(session), channel_(NULL) {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ session_->SignalState.connect(this, &TunnelSession::OnSessionState);
+ channel_ = new PseudoTcpChannel(stream_thread, session_);
+ channel_->SignalChannelClosed.connect(this, &TunnelSession::OnChannelClosed);
+}
+
+TunnelSession::~TunnelSession() {
+ ASSERT(client_ != NULL);
+ ASSERT(session_ == NULL);
+ ASSERT(channel_ == NULL);
+}
+
+talk_base::StreamInterface* TunnelSession::GetStream() {
+ ASSERT(channel_ != NULL);
+ return channel_->GetStream();
+}
+
+bool TunnelSession::HasSession(Session* session) {
+ ASSERT(NULL != session_);
+ return (session_ == session);
+}
+
+Session* TunnelSession::ReleaseSession(bool channel_exists) {
+ ASSERT(NULL != session_);
+ ASSERT(NULL != channel_);
+ Session* session = session_;
+ session_->SignalState.disconnect(this);
+ session_ = NULL;
+ if (channel_exists)
+ channel_->SignalChannelClosed.disconnect(this);
+ channel_ = NULL;
+ delete this;
+ return session;
+}
+
+void TunnelSession::OnSessionState(Session* session, Session::State state) {
+ LOG(LS_INFO) << "TunnelSession::OnSessionState("
+ << talk_base::nonnull(
+ talk_base::FindLabel(state, SESSION_STATES), "Unknown")
+ << ")";
+ ASSERT(session == session_);
+
+ switch (state) {
+ case Session::STATE_RECEIVEDINITIATE:
+ OnInitiate();
+ break;
+ case Session::STATE_SENTACCEPT:
+ case Session::STATE_RECEIVEDACCEPT:
+ OnAccept();
+ break;
+ case Session::STATE_SENTTERMINATE:
+ case Session::STATE_RECEIVEDTERMINATE:
+ OnTerminate();
+ break;
+ case Session::STATE_DEINIT:
+ // ReleaseSession should have been called before this.
+ ASSERT(false);
+ break;
+ }
+}
+
+void TunnelSession::OnInitiate() {
+ const TunnelSessionDescription* in_desc =
+ static_cast<const TunnelSessionDescription*>(
+ session_->remote_description());
+
+ ASSERT(client_ != NULL);
+ ASSERT(session_ != NULL);
+ client_->SignalIncomingTunnel(client_,
+ buzz::Jid(session_->remote_name()),
+ in_desc->description,
+ session_);
+}
+
+void TunnelSession::OnAccept() {
+ ASSERT(channel_ != NULL);
+ VERIFY(channel_->Connect("tcp"));
+}
+
+void TunnelSession::OnTerminate() {
+}
+
+void TunnelSession::OnChannelClosed(PseudoTcpChannel* channel) {
+ ASSERT(channel_ == channel);
+ ASSERT(session_ != NULL);
+ session_->Terminate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h
new file mode 100644
index 0000000..34c05bf
--- /dev/null
+++ b/Plugins/jingle/libjingle/talk/session/tunnel/tunnelsessionclient.h
@@ -0,0 +1,135 @@
+/*
+ * libjingle
+ * Copyright 2004--2006, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef __TUNNELSESSIONCLIENT_H__
+#define __TUNNELSESSIONCLIENT_H__
+
+#include <vector>
+#include "talk/base/criticalsection.h"
+#include "talk/base/stream.h"
+#include "talk/p2p/base/pseudotcp.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionclient.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/constants.h"
+
+namespace cricket {
+
+class TunnelSession;
+class TunnelStream;
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSessionClient
+///////////////////////////////////////////////////////////////////////////////
+
+class TunnelSessionClient
+ : public SessionClient, public talk_base::MessageHandler,
+ public sigslot::has_slots<> {
+public:
+ TunnelSessionClient(const buzz::Jid& jid, SessionManager* manager);
+ virtual ~TunnelSessionClient();
+
+ const buzz::Jid& jid() const { return jid_; }
+ SessionManager* session_manager() const { return session_manager_; }
+
+ const SessionDescription* CreateSessionDescription(
+ const buzz::XmlElement* element);
+ buzz::XmlElement* TranslateSessionDescription(
+ const SessionDescription* description);
+
+ void OnSessionCreate(Session* session, bool received);
+ void OnSessionDestroy(Session* session);
+
+ // This can be called on any thread. The stream interface is thread-safe, but
+ // notifications must be registered on the creating thread.
+ talk_base::StreamInterface* CreateTunnel(const buzz::Jid& to,
+ const std::string& description);
+
+ // Signal arguments are this, initiator, description, session
+ sigslot::signal4<TunnelSessionClient*, buzz::Jid, std::string, Session*>
+ SignalIncomingTunnel;
+ talk_base::StreamInterface* AcceptTunnel(Session* session);
+ void DeclineTunnel(Session* session);
+
+private:
+ void OnMessage(talk_base::Message* pmsg);
+
+ buzz::Jid jid_;
+ SessionManager* session_manager_;
+ std::vector<TunnelSession*> sessions_;
+ bool shutdown_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// TunnelSession
+// Note: The lifetime of TunnelSession is complicated. It needs to survive
+// until the following three conditions are true:
+// 1) TunnelStream has called Close (tracked via non-null stream_)
+// 2) PseudoTcp has completed (tracked via non-null tcp_)
+// 3) Session has been destroyed (tracked via non-null session_)
+// This is accomplished by calling CheckDestroy after these indicators change.
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+// TunnelStream
+// Note: Because TunnelStream provides a stream interface, it's lifetime is
+// controlled by the owner of the stream pointer. As a result, we must support
+// both the TunnelSession disappearing before TunnelStream, and vice versa.
+///////////////////////////////////////////////////////////////////////////////
+
+class PseudoTcpChannel;
+
+class TunnelSession : public sigslot::has_slots<> {
+public:
+ // Signalling thread methods
+ TunnelSession(TunnelSessionClient* client, Session* session,
+ talk_base::Thread* stream_thread);
+
+ talk_base::StreamInterface* GetStream();
+ bool HasSession(Session* session);
+ Session* ReleaseSession(bool channel_exists);
+
+private:
+ virtual ~TunnelSession();
+
+ void OnSessionState(Session* session, Session::State state);
+ void OnInitiate();
+ void OnAccept();
+ void OnTerminate();
+ void OnChannelClosed(PseudoTcpChannel* channel);
+
+ TunnelSessionClient* client_;
+ Session* session_;
+ PseudoTcpChannel* channel_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __TUNNELSESSIONCLIENT_H__