diff options
Diffstat (limited to 'Plugins/jingle/libjingle/talk/session')
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(¶ms); + 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__ |
