* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/21/21080177760a2a7a8e6a3047d96144d28d78c5ec.svn-base b/jni/pjproject-android/.svn/pristine/21/21080177760a2a7a8e6a3047d96144d28d78c5ec.svn-base
new file mode 100644
index 0000000..523162e
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21080177760a2a7a8e6a3047d96144d28d78c5ec.svn-base
@@ -0,0 +1,131 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/session.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <pj/string.h>
+
+pj_status_t session_test (pj_pool_factory *pf)
+{
+    pj_med_mgr_t *mm;
+    pj_media_session_t *s1, *s2;
+    pj_pool_t *pool;
+    pjsdp_session_desc *sdp;
+    pj_media_stream_info sd_info;
+    char buf[1024];
+    int len;
+    pj_media_stream_stat tx_stat, rx_stat;
+
+    pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
+
+    // Init media manager.
+    mm = pj_med_mgr_create ( pf );
+
+    // Create caller session.
+    // THIS WILL DEFINITELY CRASH (NULL as argument)!
+    s1 = pj_media_session_create (mm, NULL);
+
+    // Set caller's media to send-only.
+    sd_info.dir = PJMEDIA_DIR_ENCODING;
+    pj_media_session_modify_stream (s1, 0, PJMEDIA_STREAM_MODIFY_DIR, &sd_info);
+
+    // Create caller SDP.
+    sdp = pj_media_session_create_sdp (s1, pool, 0);
+    len = pjsdp_print (sdp, buf, sizeof(buf));
+    buf[len] = '\0';
+    printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Parse SDP from caller.
+    sdp = pjsdp_parse (buf, len, pool);
+
+    // Create callee session based on caller's SDP.
+    // THIS WILL DEFINITELY CRASH (NULL as argument)!
+    s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
+    
+    // Create callee SDP
+    sdp = pj_media_session_create_sdp (s2, pool, 0);
+    len = pjsdp_print (sdp, buf, sizeof(buf));
+    buf[len] = '\0';
+    printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Parse SDP from callee.
+    sdp = pjsdp_parse (buf, len, pool);
+
+    // Update caller
+    pj_media_session_update (s1, sdp);
+    sdp = pj_media_session_create_sdp (s1, pool, 0);
+    pjsdp_print (sdp, buf, sizeof(buf));
+    printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
+
+    // Now start media.
+    pj_media_session_activate (s2);
+    pj_media_session_activate (s1);
+
+    // Wait
+    for (;;) {
+	int has_stat;
+
+	printf("Enter q to exit, 1 or 2 to print statistics.\n");
+	fgets (buf, 10, stdin);
+	has_stat = 0;
+
+	switch (buf[0]) {
+	case 'q':
+	case 'Q':
+	    goto done;
+	    break;
+	case '1':
+	    pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
+	    has_stat = 1;
+	    break;
+	case '2':
+	    pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
+	    has_stat = 1;
+	    break;
+	}
+
+	if (has_stat) {
+	    pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
+	    const char *statname[2] = { "TX", "RX" };
+	    int i;
+
+	    for (i=0; i<2; ++i) {
+		printf("%s statistics:\n", statname[i]);
+		printf(" Pkt      TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
+		printf(" Octets   TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
+		printf(" Jitter   %d ms\n", stat[i]->jitter);
+		printf(" Pkt lost %d\n", stat[i]->pkt_lost);
+	    }
+	    printf("\n");
+	}
+    }
+
+done:
+
+    // Done.
+    pj_pool_release (pool);
+    pj_media_session_destroy (s2);
+    pj_media_session_destroy (s1);
+    pj_med_mgr_destroy (mm);
+
+    return 0;
+}
diff --git a/jni/pjproject-android/.svn/pristine/21/210c8cdcf5d2ab4055f0dd09e676cee9a67c30bf.svn-base b/jni/pjproject-android/.svn/pristine/21/210c8cdcf5d2ab4055f0dd09e676cee9a67c30bf.svn-base
new file mode 100644
index 0000000..e74d27c
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/210c8cdcf5d2ab4055f0dd09e676cee9a67c30bf.svn-base
@@ -0,0 +1,24 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "sip_parser.c"
diff --git a/jni/pjproject-android/.svn/pristine/21/2121626902bc7c16393fb2dc70f39b695c734456.svn-base b/jni/pjproject-android/.svn/pristine/21/2121626902bc7c16393fb2dc70f39b695c734456.svn-base
new file mode 100644
index 0000000..2af9ab1
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/2121626902bc7c16393fb2dc70f39b695c734456.svn-base
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Ticket http://trac.pjsip.org/repos/ticket/718
+# RTC doesn't put rport in Via, and it is reported to have caused segfault.
+#
+complete_msg = \
+"""MESSAGE sip:localhost SIP/2.0
+Via: SIP/2.0/UDP localhost:$LOCAL_PORT;branch=z9hG4bK$BRANCH
+From: <sip:tester@localhost>;tag=as2858a32c
+To: <sip:pjsua@localhost>
+Call-ID: 123@localhost
+CSeq: 1 MESSAGE
+Max-Forwards: 70
+Content-Length: 11
+Content-Type: text/plain
+
+Hello world
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RTC no rport", "--null-audio --auto-answer 200", 
+			    "", 200, complete_msg=complete_msg)
+
diff --git a/jni/pjproject-android/.svn/pristine/21/2121d9c84bd1a094bdce88972cab88772d79a122.svn-base b/jni/pjproject-android/.svn/pristine/21/2121d9c84bd1a094bdce88972cab88772d79a122.svn-base
new file mode 100644
index 0000000..47f753b
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/2121d9c84bd1a094bdce88972cab88772d79a122.svn-base
@@ -0,0 +1,167 @@
+/*
+ * Copyright 1992 by Jutta Degener and Carsten Bormann, Technische
+ * Universitaet Berlin.  See the accompanying file "COPYRIGHT" for
+ * details.  THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE.
+ */
+
+/* $Header: /tmp_amd/presto/export/kbs/jutta/src/gsm/RCS/gsm_print.c,v 1.1 1992/10/28 00:15:50 jutta Exp $ */
+
+#include	<stdio.h>
+
+#include "private.h"
+
+#include "gsm.h"
+#include "proto.h"
+
+int gsm_print P3((f, s, c), FILE * f, gsm s, gsm_byte * c)
+{
+	word  	LARc[8], Nc[4], Mc[4], bc[4], xmaxc[4], xmc[13*4];
+
+	/* GSM_MAGIC  = (*c >> 4) & 0xF; */
+
+	if (((*c >> 4) & 0x0F) != GSM_MAGIC) return -1;
+
+	LARc[0]  = (*c++ & 0xF) << 2;		/* 1 */
+	LARc[0] |= (*c >> 6) & 0x3;
+	LARc[1]  = *c++ & 0x3F;
+	LARc[2]  = (*c >> 3) & 0x1F;
+	LARc[3]  = (*c++ & 0x7) << 2;
+	LARc[3] |= (*c >> 6) & 0x3;
+	LARc[4]  = (*c >> 2) & 0xF;
+	LARc[5]  = (*c++ & 0x3) << 2;
+	LARc[5] |= (*c >> 6) & 0x3;
+	LARc[6]  = (*c >> 3) & 0x7;
+	LARc[7]  = *c++ & 0x7;
+
+
+	Nc[0]  = (*c >> 1) & 0x7F;
+	bc[0]  = (*c++ & 0x1) << 1;
+	bc[0] |= (*c >> 7) & 0x1;
+	Mc[0]  = (*c >> 5) & 0x3;
+	xmaxc[0]  = (*c++ & 0x1F) << 1;
+	xmaxc[0] |= (*c >> 7) & 0x1;
+	xmc[0]  = (*c >> 4) & 0x7;
+	xmc[1]  = (*c >> 1) & 0x7;
+	xmc[2]  = (*c++ & 0x1) << 2;
+	xmc[2] |= (*c >> 6) & 0x3;
+	xmc[3]  = (*c >> 3) & 0x7;
+	xmc[4]  = *c++ & 0x7;
+	xmc[5]  = (*c >> 5) & 0x7;
+	xmc[6]  = (*c >> 2) & 0x7;
+	xmc[7]  = (*c++ & 0x3) << 1;		/* 10 */
+	xmc[7] |= (*c >> 7) & 0x1;
+	xmc[8]  = (*c >> 4) & 0x7;
+	xmc[9]  = (*c >> 1) & 0x7;
+	xmc[10]  = (*c++ & 0x1) << 2;
+	xmc[10] |= (*c >> 6) & 0x3;
+	xmc[11]  = (*c >> 3) & 0x7;
+	xmc[12]  = *c++ & 0x7;
+
+	Nc[1]  = (*c >> 1) & 0x7F;
+	bc[1]  = (*c++ & 0x1) << 1;
+	bc[1] |= (*c >> 7) & 0x1;
+	Mc[1]  = (*c >> 5) & 0x3;
+	xmaxc[1]  = (*c++ & 0x1F) << 1;
+	xmaxc[1] |= (*c >> 7) & 0x1;
+	xmc[13]  = (*c >> 4) & 0x7;
+	xmc[14]  = (*c >> 1) & 0x7;
+	xmc[15]  = (*c++ & 0x1) << 2;
+	xmc[15] |= (*c >> 6) & 0x3;
+	xmc[16]  = (*c >> 3) & 0x7;
+	xmc[17]  = *c++ & 0x7;
+	xmc[18]  = (*c >> 5) & 0x7;
+	xmc[19]  = (*c >> 2) & 0x7;
+	xmc[20]  = (*c++ & 0x3) << 1;
+	xmc[20] |= (*c >> 7) & 0x1;
+	xmc[21]  = (*c >> 4) & 0x7;
+	xmc[22]  = (*c >> 1) & 0x7;
+	xmc[23]  = (*c++ & 0x1) << 2;
+	xmc[23] |= (*c >> 6) & 0x3;
+	xmc[24]  = (*c >> 3) & 0x7;
+	xmc[25]  = *c++ & 0x7;
+
+
+	Nc[2]  = (*c >> 1) & 0x7F;
+	bc[2]  = (*c++ & 0x1) << 1;		/* 20 */
+	bc[2] |= (*c >> 7) & 0x1;
+	Mc[2]  = (*c >> 5) & 0x3;
+	xmaxc[2]  = (*c++ & 0x1F) << 1;
+	xmaxc[2] |= (*c >> 7) & 0x1;
+	xmc[26]  = (*c >> 4) & 0x7;
+	xmc[27]  = (*c >> 1) & 0x7;
+	xmc[28]  = (*c++ & 0x1) << 2;
+	xmc[28] |= (*c >> 6) & 0x3;
+	xmc[29]  = (*c >> 3) & 0x7;
+	xmc[30]  = *c++ & 0x7;
+	xmc[31]  = (*c >> 5) & 0x7;
+	xmc[32]  = (*c >> 2) & 0x7;
+	xmc[33]  = (*c++ & 0x3) << 1;
+	xmc[33] |= (*c >> 7) & 0x1;
+	xmc[34]  = (*c >> 4) & 0x7;
+	xmc[35]  = (*c >> 1) & 0x7;
+	xmc[36]  = (*c++ & 0x1) << 2;
+	xmc[36] |= (*c >> 6) & 0x3;
+	xmc[37]  = (*c >> 3) & 0x7;
+	xmc[38]  = *c++ & 0x7;
+
+	Nc[3]  = (*c >> 1) & 0x7F;
+	bc[3]  = (*c++ & 0x1) << 1;
+	bc[3] |= (*c >> 7) & 0x1;
+	Mc[3]  = (*c >> 5) & 0x3;
+	xmaxc[3]  = (*c++ & 0x1F) << 1;
+	xmaxc[3] |= (*c >> 7) & 0x1;
+
+	xmc[39]  = (*c >> 4) & 0x7;
+	xmc[40]  = (*c >> 1) & 0x7;
+	xmc[41]  = (*c++ & 0x1) << 2;
+	xmc[41] |= (*c >> 6) & 0x3;
+	xmc[42]  = (*c >> 3) & 0x7;
+	xmc[43]  = *c++ & 0x7;			/* 30  */
+	xmc[44]  = (*c >> 5) & 0x7;
+	xmc[45]  = (*c >> 2) & 0x7;
+	xmc[46]  = (*c++ & 0x3) << 1;
+	xmc[46] |= (*c >> 7) & 0x1;
+	xmc[47]  = (*c >> 4) & 0x7;
+	xmc[48]  = (*c >> 1) & 0x7;
+	xmc[49]  = (*c++ & 0x1) << 2;
+	xmc[49] |= (*c >> 6) & 0x3;
+	xmc[50]  = (*c >> 3) & 0x7;
+	xmc[51]  = *c & 0x7;			/* 33 */
+
+	fprintf(f,
+	      "LARc:\t%2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d  %2.2d\n",
+	       LARc[0],LARc[1],LARc[2],LARc[3],LARc[4],LARc[5],LARc[6],LARc[7]);
+
+	fprintf(f, "#1: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
+		Nc[0], bc[0], Mc[0], xmaxc[0]);
+	fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+		xmc[0],xmc[1],xmc[2],xmc[3],xmc[4],xmc[5],xmc[6],
+		xmc[7],xmc[8],xmc[9],xmc[10],xmc[11],xmc[12] );
+
+	fprintf(f, "#2: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
+		Nc[1], bc[1], Mc[1], xmaxc[1]);
+	fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+		xmc[13+0],xmc[13+1],xmc[13+2],xmc[13+3],xmc[13+4],xmc[13+5],
+		xmc[13+6], xmc[13+7],xmc[13+8],xmc[13+9],xmc[13+10],xmc[13+11],
+		xmc[13+12] );
+
+	fprintf(f, "#3: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
+		Nc[2], bc[2], Mc[2], xmaxc[2]);
+	fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+		xmc[26+0],xmc[26+1],xmc[26+2],xmc[26+3],xmc[26+4],xmc[26+5],
+		xmc[26+6], xmc[26+7],xmc[26+8],xmc[26+9],xmc[26+10],xmc[26+11],
+		xmc[26+12] );
+
+	fprintf(f, "#4: 	Nc %4.4d    bc %d    Mc %d    xmaxc %d\n",
+		Nc[3], bc[3], Mc[3], xmaxc[3]);
+	fprintf(f,
+"\t%.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d %.2d\n",
+		xmc[39+0],xmc[39+1],xmc[39+2],xmc[39+3],xmc[39+4],xmc[39+5],
+		xmc[39+6], xmc[39+7],xmc[39+8],xmc[39+9],xmc[39+10],xmc[39+11],
+		xmc[39+12] );
+
+	return 0;
+}
diff --git a/jni/pjproject-android/.svn/pristine/21/21405bd5fa8753d87f72edb41d02fe79923ac662.svn-base b/jni/pjproject-android/.svn/pristine/21/21405bd5fa8753d87f72edb41d02fe79923ac662.svn-base
new file mode 100644
index 0000000..e48d5ec
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21405bd5fa8753d87f72edb41d02fe79923ac662.svn-base
@@ -0,0 +1,1480 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip/sip_transport_tcp.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
+#include <pj/activesock.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+/* Only declare the API if PJ_HAS_TCP is true */
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+
+
+#define THIS_FILE	"sip_transport_tcp.c"
+
+#define MAX_ASYNC_CNT	16
+#define POOL_LIS_INIT	512
+#define POOL_LIS_INC	512
+#define POOL_TP_INIT	512
+#define POOL_TP_INC	512
+
+struct tcp_listener;
+struct tcp_transport;
+
+
+/*
+ * This is the TCP listener, which is a "descendant" of pjsip_tpfactory (the
+ * SIP transport factory).
+ */
+struct tcp_listener
+{
+    pjsip_tpfactory	     factory;
+    pj_bool_t		     is_registered;
+    pjsip_endpoint	    *endpt;
+    pjsip_tpmgr		    *tpmgr;
+    pj_activesock_t	    *asock;
+    pj_sockaddr		     bound_addr;
+    pj_qos_type		     qos_type;
+    pj_qos_params	     qos_params;
+};
+
+
+/*
+ * This structure is used to keep delayed transmit operation in a list.
+ * A delayed transmission occurs when application sends tx_data when
+ * the TCP connect/establishment is still in progress. These delayed
+ * transmission will be "flushed" once the socket is connected (either
+ * successfully or with errors).
+ */
+struct delayed_tdata
+{
+    PJ_DECL_LIST_MEMBER(struct delayed_tdata);
+    pjsip_tx_data_op_key    *tdata_op_key;
+    pj_time_val              timeout;
+};
+
+
+/*
+ * This structure describes the TCP transport, and it's descendant of
+ * pjsip_transport.
+ */
+struct tcp_transport
+{
+    pjsip_transport	     base;
+    pj_bool_t		     is_server;
+
+    /* Do not save listener instance in the transport, because
+     * listener might be destroyed during transport's lifetime.
+     * See http://trac.pjsip.org/repos/ticket/491
+    struct tcp_listener	    *listener;
+     */
+
+    pj_bool_t		     is_registered;
+    pj_bool_t		     is_closing;
+    pj_status_t		     close_reason;
+    pj_sock_t		     sock;
+    pj_activesock_t	    *asock;
+    pj_bool_t		     has_pending_connect;
+
+    /* Keep-alive timer. */
+    pj_timer_entry	     ka_timer;
+    pj_time_val		     last_activity;
+    pjsip_tx_data_op_key     ka_op_key;
+    pj_str_t		     ka_pkt;
+
+    /* TCP transport can only have  one rdata!
+     * Otherwise chunks of incoming PDU may be received on different
+     * buffer.
+     */
+    pjsip_rx_data	     rdata;
+
+    /* Pending transmission list. */
+    struct delayed_tdata     delayed_list;
+};
+
+
+/****************************************************************************
+ * PROTOTYPES
+ */
+
+/* This callback is called when pending accept() operation completes. */
+static pj_bool_t on_accept_complete(pj_activesock_t *asock,
+				    pj_sock_t newsock,
+				    const pj_sockaddr_t *src_addr,
+				    int src_addr_len);
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory);
+
+/* This callback is called by transport manager to create transport */
+static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
+					pjsip_tpmgr *mgr,
+					pjsip_endpoint *endpt,
+					const pj_sockaddr *rem_addr,
+					int addr_len,
+					pjsip_transport **transport);
+
+/* Common function to create and initialize transport */
+static pj_status_t tcp_create(struct tcp_listener *listener,
+			      pj_pool_t *pool,
+			      pj_sock_t sock, pj_bool_t is_server,
+			      const pj_sockaddr *local,
+			      const pj_sockaddr *remote,
+			      struct tcp_transport **p_tcp);
+
+
+static void tcp_perror(const char *sender, const char *title,
+		       pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
+}
+
+
+static void sockaddr_to_host_port( pj_pool_t *pool,
+				   pjsip_host_port *host_port,
+				   const pj_sockaddr *addr )
+{
+    host_port->host.ptr = (char*) pj_pool_alloc(pool, PJ_INET6_ADDRSTRLEN+4);
+    pj_sockaddr_print(addr, host_port->host.ptr, PJ_INET6_ADDRSTRLEN+4, 0);
+    host_port->host.slen = pj_ansi_strlen(host_port->host.ptr);
+    host_port->port = pj_sockaddr_get_port(addr);
+}
+
+
+static void tcp_init_shutdown(struct tcp_transport *tcp, pj_status_t status)
+{
+    pjsip_tp_state_callback state_cb;
+
+    if (tcp->close_reason == PJ_SUCCESS)
+	tcp->close_reason = status;
+
+    if (tcp->base.is_shutdown || tcp->base.is_destroying)
+	return;
+
+    /* Prevent immediate transport destroy by application, as transport
+     * state notification callback may be stacked and transport instance
+     * must remain valid at any point in the callback.
+     */
+    pjsip_transport_add_ref(&tcp->base);
+
+    /* Notify application of transport disconnected state */
+    state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr);
+    if (state_cb) {
+	pjsip_transport_state_info state_info;
+
+	pj_bzero(&state_info, sizeof(state_info));
+	state_info.status = tcp->close_reason;
+	(*state_cb)(&tcp->base, PJSIP_TP_STATE_DISCONNECTED, &state_info);
+    }
+
+    /* check again */
+    if (tcp->base.is_shutdown || tcp->base.is_destroying)
+	return;
+
+    /* We can not destroy the transport since high level objects may
+     * still keep reference to this transport. So we can only 
+     * instruct transport manager to gracefully start the shutdown
+     * procedure for this transport.
+     */
+    pjsip_transport_shutdown(&tcp->base);
+
+    /* Now, it is ok to destroy the transport. */
+    pjsip_transport_dec_ref(&tcp->base);
+}
+
+
+/*
+ * Initialize pjsip_tcp_transport_cfg structure with default values.
+ */
+PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg,
+					     int af)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+    cfg->af = af;
+    pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0);
+    cfg->async_cnt = 1;
+    cfg->reuse_addr = PJSIP_TCP_TRANSPORT_REUSEADDR;
+}
+
+
+/****************************************************************************
+ * The TCP listener/transport factory.
+ */
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start3(
+					pjsip_endpoint *endpt,
+					const pjsip_tcp_transport_cfg *cfg,
+					pjsip_tpfactory **p_factory
+					)
+{
+    pj_pool_t *pool;
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    struct tcp_listener *listener;
+    pj_activesock_cfg asock_cfg;
+    pj_activesock_cb listener_cb;
+    pj_sockaddr *listener_addr;
+    int addr_len;
+    pj_status_t status;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL);
+
+    /* Verify that address given in a_name (if any) is valid */
+    if (cfg->addr_name.host.slen) {
+	pj_sockaddr tmp;
+
+	status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, 
+				  (pj_uint16_t)cfg->addr_name.port);
+	if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) ||
+	    (cfg->af==pj_AF_INET() && 
+	     tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) 
+	{
+	    /* Invalid address */
+	    return PJ_EINVAL;
+	}
+    }
+
+    pool = pjsip_endpt_create_pool(endpt, "tcplis", POOL_LIS_INIT, 
+				   POOL_LIS_INC);
+    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
+
+
+    listener = PJ_POOL_ZALLOC_T(pool, struct tcp_listener);
+    listener->factory.pool = pool;
+    listener->factory.type = cfg->af==pj_AF_INET() ? PJSIP_TRANSPORT_TCP :
+						     PJSIP_TRANSPORT_TCP6;
+    listener->factory.type_name = (char*)
+		pjsip_transport_get_type_name(listener->factory.type);
+    listener->factory.flag = 
+	pjsip_transport_get_flag_from_type(listener->factory.type);
+    listener->qos_type = cfg->qos_type;
+    pj_memcpy(&listener->qos_params, &cfg->qos_params,
+	      sizeof(cfg->qos_params));
+
+    pj_ansi_strcpy(listener->factory.obj_name, "tcplis");
+    if (listener->factory.type==PJSIP_TRANSPORT_TCP6)
+	pj_ansi_strcat(listener->factory.obj_name, "6");
+
+    status = pj_lock_create_recursive_mutex(pool, listener->factory.obj_name,
+					    &listener->factory.lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+
+    /* Create socket */
+    status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, 
+				2, listener->factory.obj_name, 
+				"SIP TCP listener socket");
+
+    /* Apply SO_REUSEADDR */
+    if (cfg->reuse_addr) {
+	int enabled = 1;
+	status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_REUSEADDR(),
+				    &enabled, sizeof(enabled));
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(listener->factory.obj_name, status,
+		         "Warning: error applying SO_REUSEADDR"));
+	}
+    }
+
+    /* Bind address may be different than factory.local_addr because
+     * factory.local_addr will be resolved below.
+     */
+    pj_sockaddr_cp(&listener->bound_addr, &cfg->bind_addr);
+
+    /* Bind socket */
+    listener_addr = &listener->factory.local_addr;
+    pj_sockaddr_cp(listener_addr, &cfg->bind_addr);
+
+    status = pj_sock_bind(sock, listener_addr, 
+			  pj_sockaddr_get_len(listener_addr));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Retrieve the bound address */
+    addr_len = pj_sockaddr_get_len(listener_addr);
+    status = pj_sock_getsockname(sock, listener_addr, &addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* If published host/IP is specified, then use that address as the
+     * listener advertised address.
+     */
+    if (cfg->addr_name.host.slen) {
+	/* Copy the address */
+	listener->factory.addr_name = cfg->addr_name;
+	pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, 
+		  &cfg->addr_name.host);
+	listener->factory.addr_name.port = cfg->addr_name.port;
+
+    } else {
+	/* No published address is given, use the bound address */
+
+	/* If the address returns 0.0.0.0, use the default
+	 * interface address as the transport's address.
+	 */
+	if (!pj_sockaddr_has_addr(listener_addr)) {
+	    pj_sockaddr hostip;
+
+	    status = pj_gethostip(listener->bound_addr.addr.sa_family,
+	                          &hostip);
+	    if (status != PJ_SUCCESS)
+		goto on_error;
+
+	    pj_sockaddr_copy_addr(listener_addr, &hostip);
+	}
+
+	/* Save the address name */
+	sockaddr_to_host_port(listener->factory.pool, 
+			      &listener->factory.addr_name, 
+			      listener_addr);
+    }
+
+    /* If port is zero, get the bound port */
+    if (listener->factory.addr_name.port == 0) {
+	listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr);
+    }
+
+    pj_ansi_snprintf(listener->factory.obj_name, 
+		     sizeof(listener->factory.obj_name),
+		     "tcplis:%d",  listener->factory.addr_name.port);
+
+
+    /* Start listening to the address */
+    status = pj_sock_listen(sock, PJSIP_TCP_TRANSPORT_BACKLOG);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    if (cfg->async_cnt > MAX_ASYNC_CNT) 
+	asock_cfg.async_cnt = MAX_ASYNC_CNT;
+    else
+	asock_cfg.async_cnt = cfg->async_cnt;
+
+    pj_bzero(&listener_cb, sizeof(listener_cb));
+    listener_cb.on_accept_complete = &on_accept_complete;
+    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg,
+				  pjsip_endpt_get_ioqueue(endpt), 
+				  &listener_cb, listener,
+				  &listener->asock);
+
+    /* Register to transport manager */
+    listener->endpt = endpt;
+    listener->tpmgr = pjsip_endpt_get_tpmgr(endpt);
+    listener->factory.create_transport = lis_create_transport;
+    listener->factory.destroy = lis_destroy;
+    listener->is_registered = PJ_TRUE;
+    status = pjsip_tpmgr_register_tpfactory(listener->tpmgr,
+					    &listener->factory);
+    if (status != PJ_SUCCESS) {
+	listener->is_registered = PJ_FALSE;
+	goto on_error;
+    }
+
+    /* Start pending accept() operations */
+    status = pj_activesock_start_accept(listener->asock, pool);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    PJ_LOG(4,(listener->factory.obj_name, 
+	     "SIP TCP listener ready for incoming connections at %.*s:%d",
+	     (int)listener->factory.addr_name.host.slen,
+	     listener->factory.addr_name.host.ptr,
+	     listener->factory.addr_name.port));
+
+    /* Return the pointer to user */
+    if (p_factory) *p_factory = &listener->factory;
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (listener->asock==NULL && sock!=PJ_INVALID_SOCKET)
+	pj_sock_close(sock);
+    lis_destroy(&listener->factory);
+    return status;
+}
+
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt,
+					       const pj_sockaddr_in *local,
+					       const pjsip_host_port *a_name,
+					       unsigned async_cnt,
+					       pjsip_tpfactory **p_factory)
+{
+    pjsip_tcp_transport_cfg cfg;
+
+    pjsip_tcp_transport_cfg_default(&cfg, pj_AF_INET());
+
+    if (local)
+	pj_sockaddr_cp(&cfg.bind_addr, local);
+    else
+	pj_sockaddr_init(cfg.af, &cfg.bind_addr, NULL, 0);
+
+    if (a_name)
+	pj_memcpy(&cfg.addr_name, a_name, sizeof(*a_name));
+
+    if (async_cnt)
+	cfg.async_cnt = async_cnt;
+
+    return pjsip_tcp_transport_start3(endpt, &cfg, p_factory);
+}
+
+
+/*
+ * This is the public API to create, initialize, register, and start the
+ * TCP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_tcp_transport_start( pjsip_endpoint *endpt,
+					       const pj_sockaddr_in *local,
+					       unsigned async_cnt,
+					       pjsip_tpfactory **p_factory)
+{
+    return pjsip_tcp_transport_start2(endpt, local, NULL, async_cnt, p_factory);
+}
+
+
+/* This callback is called by transport manager to destroy listener */
+static pj_status_t lis_destroy(pjsip_tpfactory *factory)
+{
+    struct tcp_listener *listener = (struct tcp_listener *)factory;
+
+    if (listener->is_registered) {
+	pjsip_tpmgr_unregister_tpfactory(listener->tpmgr, &listener->factory);
+	listener->is_registered = PJ_FALSE;
+    }
+
+    if (listener->asock) {
+	pj_activesock_close(listener->asock);
+	listener->asock = NULL;
+    }
+
+    if (listener->factory.lock) {
+	pj_lock_destroy(listener->factory.lock);
+	listener->factory.lock = NULL;
+    }
+
+    if (listener->factory.pool) {
+	pj_pool_t *pool = listener->factory.pool;
+
+	PJ_LOG(4,(listener->factory.obj_name,  "SIP TCP listener destroyed"));
+
+	listener->factory.pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/***************************************************************************/
+/*
+ * TCP Transport
+ */
+
+/*
+ * Prototypes.
+ */
+/* Called by transport manager to send message */
+static pj_status_t tcp_send_msg(pjsip_transport *transport, 
+				pjsip_tx_data *tdata,
+				const pj_sockaddr_t *rem_addr,
+				int addr_len,
+				void *token,
+				pjsip_transport_callback callback);
+
+/* Called by transport manager to shutdown */
+static pj_status_t tcp_shutdown(pjsip_transport *transport);
+
+/* Called by transport manager to destroy transport */
+static pj_status_t tcp_destroy_transport(pjsip_transport *transport);
+
+/* Utility to destroy transport */
+static pj_status_t tcp_destroy(pjsip_transport *transport,
+			       pj_status_t reason);
+
+/* Callback on incoming data */
+static pj_bool_t on_data_read(pj_activesock_t *asock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder);
+
+/* Callback when packet is sent */
+static pj_bool_t on_data_sent(pj_activesock_t *asock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
+
+/* Callback when connect completes */
+static pj_bool_t on_connect_complete(pj_activesock_t *asock,
+				     pj_status_t status);
+
+/* TCP keep-alive timer callback */
+static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e);
+
+/*
+ * Common function to create TCP transport, called when pending accept() and
+ * pending connect() complete.
+ */
+static pj_status_t tcp_create( struct tcp_listener *listener,
+			       pj_pool_t *pool,
+			       pj_sock_t sock, pj_bool_t is_server,
+			       const pj_sockaddr *local,
+			       const pj_sockaddr *remote,
+			       struct tcp_transport **p_tcp)
+{
+    struct tcp_transport *tcp;
+    pj_ioqueue_t *ioqueue;
+    pj_activesock_cfg asock_cfg;
+    pj_activesock_cb tcp_callback;
+    const pj_str_t ka_pkt = PJSIP_TCP_KEEP_ALIVE_DATA;
+    char print_addr[PJ_INET6_ADDRSTRLEN+10];
+    pj_status_t status;
+    
+
+    PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_EINVAL);
+
+
+    if (pool == NULL) {
+	pool = pjsip_endpt_create_pool(listener->endpt, "tcp",
+				       POOL_TP_INIT, POOL_TP_INC);
+	PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+    }    
+
+    /*
+     * Create and initialize basic transport structure.
+     */
+    tcp = PJ_POOL_ZALLOC_T(pool, struct tcp_transport);
+    tcp->is_server = is_server;
+    tcp->sock = sock;
+    /*tcp->listener = listener;*/
+    pj_list_init(&tcp->delayed_list);
+    tcp->base.pool = pool;
+
+    pj_ansi_snprintf(tcp->base.obj_name, PJ_MAX_OBJ_NAME, 
+		     (is_server ? "tcps%p" :"tcpc%p"), tcp);
+
+    status = pj_atomic_create(pool, 0, &tcp->base.ref_cnt);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    status = pj_lock_create_recursive_mutex(pool, "tcp", &tcp->base.lock);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    tcp->base.key.type = listener->factory.type;
+    pj_sockaddr_cp(&tcp->base.key.rem_addr, remote);
+    tcp->base.type_name = (char*)pjsip_transport_get_type_name(
+				(pjsip_transport_type_e)tcp->base.key.type);
+    tcp->base.flag = pjsip_transport_get_flag_from_type(
+				(pjsip_transport_type_e)tcp->base.key.type);
+
+    tcp->base.info = (char*) pj_pool_alloc(pool, 64);
+    pj_ansi_snprintf(tcp->base.info, 64, "%s to %s",
+                     tcp->base.type_name,
+                     pj_sockaddr_print(remote, print_addr,
+                                       sizeof(print_addr), 3));
+
+    tcp->base.addr_len = pj_sockaddr_get_len(remote);
+    pj_sockaddr_cp(&tcp->base.local_addr, local);
+    sockaddr_to_host_port(pool, &tcp->base.local_name, local);
+    sockaddr_to_host_port(pool, &tcp->base.remote_name, remote);
+    tcp->base.dir = is_server? PJSIP_TP_DIR_INCOMING : PJSIP_TP_DIR_OUTGOING;
+
+    tcp->base.endpt = listener->endpt;
+    tcp->base.tpmgr = listener->tpmgr;
+    tcp->base.send_msg = &tcp_send_msg;
+    tcp->base.do_shutdown = &tcp_shutdown;
+    tcp->base.destroy = &tcp_destroy_transport;
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = 1;
+
+    pj_bzero(&tcp_callback, sizeof(tcp_callback));
+    tcp_callback.on_data_read = &on_data_read;
+    tcp_callback.on_data_sent = &on_data_sent;
+    tcp_callback.on_connect_complete = &on_connect_complete;
+
+    ioqueue = pjsip_endpt_get_ioqueue(listener->endpt);
+    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), &asock_cfg,
+				  ioqueue, &tcp_callback, tcp, &tcp->asock);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    /* Register transport to transport manager */
+    status = pjsip_transport_register(listener->tpmgr, &tcp->base);
+    if (status != PJ_SUCCESS) {
+	goto on_error;
+    }
+
+    tcp->is_registered = PJ_TRUE;
+
+    /* Initialize keep-alive timer */
+    tcp->ka_timer.user_data = (void*)tcp;
+    tcp->ka_timer.cb = &tcp_keep_alive_timer;
+    pj_ioqueue_op_key_init(&tcp->ka_op_key.key, sizeof(pj_ioqueue_op_key_t));
+    pj_strdup(tcp->base.pool, &tcp->ka_pkt, &ka_pkt);
+
+    /* Done setting up basic transport. */
+    *p_tcp = tcp;
+
+    PJ_LOG(4,(tcp->base.obj_name, "TCP %s transport created",
+	      (tcp->is_server ? "server" : "client")));
+
+    return PJ_SUCCESS;
+
+on_error:
+    tcp_destroy(&tcp->base, status);
+    return status;
+}
+
+
+/* Flush all delayed transmision once the socket is connected. */
+static void tcp_flush_pending_tx(struct tcp_transport *tcp)
+{
+    pj_time_val now;
+
+    pj_gettickcount(&now);
+    pj_lock_acquire(tcp->base.lock);
+    while (!pj_list_empty(&tcp->delayed_list)) {
+	struct delayed_tdata *pending_tx;
+	pjsip_tx_data *tdata;
+	pj_ioqueue_op_key_t *op_key;
+	pj_ssize_t size;
+	pj_status_t status;
+
+	pending_tx = tcp->delayed_list.next;
+	pj_list_erase(pending_tx);
+
+	tdata = pending_tx->tdata_op_key->tdata;
+	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+        if (pending_tx->timeout.sec > 0 &&
+            PJ_TIME_VAL_GT(now, pending_tx->timeout))
+        {
+            continue;
+        }
+
+	/* send! */
+	size = tdata->buf.cur - tdata->buf.start;
+	status = pj_activesock_send(tcp->asock, op_key, tdata->buf.start, 
+				    &size, 0);
+	if (status != PJ_EPENDING) {
+            pj_lock_release(tcp->base.lock);
+	    on_data_sent(tcp->asock, op_key, size);
+            pj_lock_acquire(tcp->base.lock);
+	}
+
+    }
+    pj_lock_release(tcp->base.lock);
+}
+
+
+/* Called by transport manager to destroy transport */
+static pj_status_t tcp_destroy_transport(pjsip_transport *transport)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*)transport;
+
+    /* Transport would have been unregistered by now since this callback
+     * is called by transport manager.
+     */
+    tcp->is_registered = PJ_FALSE;
+
+    return tcp_destroy(transport, tcp->close_reason);
+}
+
+
+/* Destroy TCP transport */
+static pj_status_t tcp_destroy(pjsip_transport *transport, 
+			       pj_status_t reason)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*)transport;
+
+    if (tcp->close_reason == 0)
+	tcp->close_reason = reason;
+
+    if (tcp->is_registered) {
+	tcp->is_registered = PJ_FALSE;
+	pjsip_transport_destroy(transport);
+
+	/* pjsip_transport_destroy will recursively call this function
+	 * again.
+	 */
+	return PJ_SUCCESS;
+    }
+
+    /* Mark transport as closing */
+    tcp->is_closing = PJ_TRUE;
+
+    /* Stop keep-alive timer. */
+    if (tcp->ka_timer.id) {
+	pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->ka_timer);
+	tcp->ka_timer.id = PJ_FALSE;
+    }
+
+    /* Cancel all delayed transmits */
+    while (!pj_list_empty(&tcp->delayed_list)) {
+	struct delayed_tdata *pending_tx;
+	pj_ioqueue_op_key_t *op_key;
+
+	pending_tx = tcp->delayed_list.next;
+	pj_list_erase(pending_tx);
+
+	op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+	on_data_sent(tcp->asock, op_key, -reason);
+    }
+
+    if (tcp->rdata.tp_info.pool) {
+	pj_pool_release(tcp->rdata.tp_info.pool);
+	tcp->rdata.tp_info.pool = NULL;
+    }
+
+    if (tcp->asock) {
+	pj_activesock_close(tcp->asock);
+	tcp->asock = NULL;
+	tcp->sock = PJ_INVALID_SOCKET;
+    } else if (tcp->sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(tcp->sock);
+	tcp->sock = PJ_INVALID_SOCKET;
+    }
+
+    if (tcp->base.lock) {
+	pj_lock_destroy(tcp->base.lock);
+	tcp->base.lock = NULL;
+    }
+
+    if (tcp->base.ref_cnt) {
+	pj_atomic_destroy(tcp->base.ref_cnt);
+	tcp->base.ref_cnt = NULL;
+    }
+
+    if (tcp->base.pool) {
+	pj_pool_t *pool;
+
+	if (reason != PJ_SUCCESS) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+
+	    pj_strerror(reason, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(tcp->base.obj_name, 
+		      "TCP transport destroyed with reason %d: %s", 
+		      reason, errmsg));
+
+	} else {
+
+	    PJ_LOG(4,(tcp->base.obj_name, 
+		      "TCP transport destroyed normally"));
+
+	}
+
+	pool = tcp->base.pool;
+	tcp->base.pool = NULL;
+	pj_pool_release(pool);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This utility function creates receive data buffers and start
+ * asynchronous recv() operations from the socket. It is called after
+ * accept() or connect() operation complete.
+ */
+static pj_status_t tcp_start_read(struct tcp_transport *tcp)
+{
+    pj_pool_t *pool;
+    pj_uint32_t size;
+    pj_sockaddr *rem_addr;
+    void *readbuf[1];
+    pj_status_t status;
+
+    /* Init rdata */
+    pool = pjsip_endpt_create_pool(tcp->base.endpt,
+				   "rtd%p",
+				   PJSIP_POOL_RDATA_LEN,
+				   PJSIP_POOL_RDATA_INC);
+    if (!pool) {
+	tcp_perror(tcp->base.obj_name, "Unable to create pool", PJ_ENOMEM);
+	return PJ_ENOMEM;
+    }
+
+    tcp->rdata.tp_info.pool = pool;
+
+    tcp->rdata.tp_info.transport = &tcp->base;
+    tcp->rdata.tp_info.tp_data = tcp;
+    tcp->rdata.tp_info.op_key.rdata = &tcp->rdata;
+    pj_ioqueue_op_key_init(&tcp->rdata.tp_info.op_key.op_key, 
+			   sizeof(pj_ioqueue_op_key_t));
+
+    tcp->rdata.pkt_info.src_addr = tcp->base.key.rem_addr;
+    tcp->rdata.pkt_info.src_addr_len = sizeof(tcp->rdata.pkt_info.src_addr);
+    rem_addr = &tcp->base.key.rem_addr;
+    pj_sockaddr_print(rem_addr, tcp->rdata.pkt_info.src_name,
+                      sizeof(tcp->rdata.pkt_info.src_name), 0);
+    tcp->rdata.pkt_info.src_port = pj_sockaddr_get_port(rem_addr);
+
+    size = sizeof(tcp->rdata.pkt_info.packet);
+    readbuf[0] = tcp->rdata.pkt_info.packet;
+    status = pj_activesock_start_read2(tcp->asock, tcp->base.pool, size,
+				       readbuf, 0);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	PJ_LOG(4, (tcp->base.obj_name, 
+		   "pj_activesock_start_read() error, status=%d", 
+		   status));
+	return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* This callback is called by transport manager for the TCP factory
+ * to create outgoing transport to the specified destination.
+ */
+static pj_status_t lis_create_transport(pjsip_tpfactory *factory,
+					pjsip_tpmgr *mgr,
+					pjsip_endpoint *endpt,
+					const pj_sockaddr *rem_addr,
+					int addr_len,
+					pjsip_transport **p_transport)
+{
+    struct tcp_listener *listener;
+    struct tcp_transport *tcp;
+    pj_sock_t sock;
+    pj_sockaddr local_addr;
+    pj_status_t status;
+
+    /* Sanity checks */
+    PJ_ASSERT_RETURN(factory && mgr && endpt && rem_addr &&
+		     addr_len && p_transport, PJ_EINVAL);
+
+    /* Check that address is a sockaddr_in or sockaddr_in6*/
+    PJ_ASSERT_RETURN((rem_addr->addr.sa_family == pj_AF_INET() &&
+		      addr_len == sizeof(pj_sockaddr_in)) ||
+		     (rem_addr->addr.sa_family == pj_AF_INET6() &&
+		      addr_len == sizeof(pj_sockaddr_in6)), PJ_EINVAL);
+
+
+    listener = (struct tcp_listener*)factory;
+
+    /* Create socket */
+    status = pj_sock_socket(rem_addr->addr.sa_family, pj_SOCK_STREAM(),
+                            0, &sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(sock, listener->qos_type, 
+				&listener->qos_params, 
+				2, listener->factory.obj_name, 
+				"outgoing SIP TCP socket");
+
+    /* Bind to listener's address and any port */
+    pj_bzero(&local_addr, sizeof(local_addr));
+    pj_sockaddr_cp(&local_addr, &listener->bound_addr);
+    pj_sockaddr_set_port(&local_addr, 0);
+
+    status = pj_sock_bind(sock, &local_addr,
+                          pj_sockaddr_get_len(&local_addr));
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock);
+	return status;
+    }
+
+    /* Get the local port */
+    addr_len = sizeof(local_addr);
+    status = pj_sock_getsockname(sock, &local_addr, &addr_len);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock);
+	return status;
+    }
+
+    /* Initially set the address from the listener's address */
+    if (!pj_sockaddr_has_addr(&local_addr)) {
+	pj_sockaddr_copy_addr(&local_addr, &listener->factory.local_addr);
+    }
+
+    /* Create the transport descriptor */
+    status = tcp_create(listener, NULL, sock, PJ_FALSE, &local_addr, 
+			rem_addr, &tcp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Start asynchronous connect() operation */
+    tcp->has_pending_connect = PJ_TRUE;
+    status = pj_activesock_start_connect(tcp->asock, tcp->base.pool, rem_addr,
+					 addr_len);
+    if (status == PJ_SUCCESS) {
+	on_connect_complete(tcp->asock, PJ_SUCCESS);
+    } else if (status != PJ_EPENDING) {
+	tcp_destroy(&tcp->base, status);
+	return status;
+    }
+
+    if (tcp->has_pending_connect) {
+	/* Update (again) local address, just in case local address currently
+	 * set is different now that asynchronous connect() is started.
+	 */
+	addr_len = sizeof(local_addr);
+	if (pj_sock_getsockname(sock, &local_addr, &addr_len)==PJ_SUCCESS) {
+	    pj_sockaddr *tp_addr = &tcp->base.local_addr;
+
+	    /* Some systems (like old Win32 perhaps) may not set local address
+	     * properly before socket is fully connected.
+	     */
+	    if (pj_sockaddr_cmp(tp_addr, &local_addr) &&
+                pj_sockaddr_has_addr(&local_addr) &&
+		pj_sockaddr_get_port(&local_addr) != 0)
+	    {
+		pj_sockaddr_cp(tp_addr, &local_addr);
+		sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name,
+				      &local_addr);
+	    }
+	}
+	
+	PJ_LOG(4,(tcp->base.obj_name, 
+		  "TCP transport %.*s:%d is connecting to %.*s:%d...",
+		  (int)tcp->base.local_name.host.slen,
+		  tcp->base.local_name.host.ptr,
+		  tcp->base.local_name.port,
+		  (int)tcp->base.remote_name.host.slen,
+		  tcp->base.remote_name.host.ptr,
+		  tcp->base.remote_name.port));
+    }
+
+    /* Done */
+    *p_transport = &tcp->base;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * This callback is called by active socket when pending accept() operation
+ * has completed.
+ */
+static pj_bool_t on_accept_complete(pj_activesock_t *asock,
+				    pj_sock_t sock,
+				    const pj_sockaddr_t *src_addr,
+				    int src_addr_len)
+{
+    struct tcp_listener *listener;
+    struct tcp_transport *tcp;
+    char addr[PJ_INET6_ADDRSTRLEN+10];
+    pjsip_tp_state_callback state_cb;
+    pj_sockaddr tmp_src_addr;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(src_addr_len);
+
+    listener = (struct tcp_listener*) pj_activesock_get_user_data(asock);
+
+    PJ_ASSERT_RETURN(sock != PJ_INVALID_SOCKET, PJ_TRUE);
+
+    PJ_LOG(4,(listener->factory.obj_name, 
+	      "TCP listener %.*s:%d: got incoming TCP connection "
+	      "from %s, sock=%d",
+	      (int)listener->factory.addr_name.host.slen,
+	      listener->factory.addr_name.host.ptr,
+	      listener->factory.addr_name.port,
+	      pj_sockaddr_print(src_addr, addr, sizeof(addr), 3),
+	      sock));
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(sock, listener->qos_type, 
+				&listener->qos_params, 
+				2, listener->factory.obj_name, 
+				"incoming SIP TCP socket");
+
+    /* tcp_create() expect pj_sockaddr, so copy src_addr to temporary var,
+     * just in case.
+     */
+    pj_bzero(&tmp_src_addr, sizeof(tmp_src_addr));
+    pj_sockaddr_cp(&tmp_src_addr, src_addr);
+
+    /* 
+     * Incoming connection!
+     * Create TCP transport for the new socket.
+     */
+    status = tcp_create( listener, NULL, sock, PJ_TRUE,
+			 &listener->factory.local_addr,
+			 &tmp_src_addr, &tcp);
+    if (status == PJ_SUCCESS) {
+	status = tcp_start_read(tcp);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(3,(tcp->base.obj_name, "New transport cancelled"));
+	    tcp_destroy(&tcp->base, status);
+	} else {
+	    /* Start keep-alive timer */
+	    if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+		pj_time_val delay = {PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0};
+		pjsip_endpt_schedule_timer(listener->endpt, 
+					   &tcp->ka_timer, 
+					   &delay);
+		tcp->ka_timer.id = PJ_TRUE;
+		pj_gettimeofday(&tcp->last_activity);
+	    }
+
+	    /* Notify application of transport state accepted */
+	    state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr);
+	    if (state_cb) {
+		pjsip_transport_state_info state_info;
+            
+		pj_bzero(&state_info, sizeof(state_info));
+		(*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info);
+	    }
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+
+/* 
+ * Callback from ioqueue when packet is sent.
+ */
+static pj_bool_t on_data_sent(pj_activesock_t *asock,
+			      pj_ioqueue_op_key_t *op_key,
+			      pj_ssize_t bytes_sent)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) 
+    				pj_activesock_get_user_data(asock);
+    pjsip_tx_data_op_key *tdata_op_key = (pjsip_tx_data_op_key*)op_key;
+
+    /* Note that op_key may be the op_key from keep-alive, thus
+     * it will not have tdata etc.
+     */
+
+    tdata_op_key->tdata = NULL;
+
+    if (tdata_op_key->callback) {
+	/*
+	 * Notify sip_transport.c that packet has been sent.
+	 */
+	if (bytes_sent == 0)
+	    bytes_sent = -PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
+
+	tdata_op_key->callback(&tcp->base, tdata_op_key->token, bytes_sent);
+
+	/* Mark last activity time */
+	pj_gettimeofday(&tcp->last_activity);
+
+    }
+
+    /* Check for error/closure */
+    if (bytes_sent <= 0) {
+	pj_status_t status;
+
+	PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d", 
+		  bytes_sent));
+
+	status = (bytes_sent == 0) ? PJ_RETURN_OS_ERROR(OSERR_ENOTCONN) :
+				     (pj_status_t)-bytes_sent;
+
+	tcp_init_shutdown(tcp, status);
+
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+
+/* 
+ * This callback is called by transport manager to send SIP message 
+ */
+static pj_status_t tcp_send_msg(pjsip_transport *transport, 
+				pjsip_tx_data *tdata,
+				const pj_sockaddr_t *rem_addr,
+				int addr_len,
+				void *token,
+				pjsip_transport_callback callback)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*)transport;
+    pj_ssize_t size;
+    pj_bool_t delayed = PJ_FALSE;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Sanity check */
+    PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL);
+
+    /* Check that there's no pending operation associated with the tdata */
+    PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX);
+    
+    /* Check the address is supported */
+    PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) ||
+	                          addr_len==sizeof(pj_sockaddr_in6)),
+	             PJ_EINVAL);
+
+    /* Init op key. */
+    tdata->op_key.tdata = tdata;
+    tdata->op_key.token = token;
+    tdata->op_key.callback = callback;
+
+    /* If asynchronous connect() has not completed yet, just put the
+     * transmit data in the pending transmission list since we can not
+     * use the socket yet.
+     */
+    if (tcp->has_pending_connect) {
+
+	/*
+	 * Looks like connect() is still in progress. Check again (this time
+	 * with holding the lock) to be sure.
+	 */
+	pj_lock_acquire(tcp->base.lock);
+
+	if (tcp->has_pending_connect) {
+	    struct delayed_tdata *delayed_tdata;
+
+	    /*
+	     * connect() is still in progress. Put the transmit data to
+	     * the delayed list.
+             * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583),
+             * we also add timeout value for the transmit data. When the
+             * connect() is completed, the timeout value will be checked to
+             * determine whether the transmit data needs to be sent.
+	     */
+	    delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, 
+					     struct delayed_tdata);
+	    delayed_tdata->tdata_op_key = &tdata->op_key;
+            if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) {
+                pj_gettickcount(&delayed_tdata->timeout);
+                delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td;
+                pj_time_val_normalize(&delayed_tdata->timeout);
+            }
+
+	    pj_list_push_back(&tcp->delayed_list, delayed_tdata);
+	    status = PJ_EPENDING;
+
+	    /* Prevent pj_ioqueue_send() to be called below */
+	    delayed = PJ_TRUE;
+	}
+
+	pj_lock_release(tcp->base.lock);
+    } 
+    
+    if (!delayed) {
+	/*
+	 * Transport is ready to go. Send the packet to ioqueue to be
+	 * sent asynchronously.
+	 */
+	size = tdata->buf.cur - tdata->buf.start;
+	status = pj_activesock_send(tcp->asock, 
+				    (pj_ioqueue_op_key_t*)&tdata->op_key,
+				    tdata->buf.start, &size, 0);
+
+	if (status != PJ_EPENDING) {
+	    /* Not pending (could be immediate success or error) */
+	    tdata->op_key.tdata = NULL;
+
+	    /* Shutdown transport on closure/errors */
+	    if (size <= 0) {
+
+		PJ_LOG(5,(tcp->base.obj_name, "TCP send() error, sent=%d", 
+			  size));
+
+		if (status == PJ_SUCCESS) 
+		    status = PJ_RETURN_OS_ERROR(OSERR_ENOTCONN);
+
+		tcp_init_shutdown(tcp, status);
+	    }
+	}
+    }
+
+    return status;
+}
+
+
+/* 
+ * This callback is called by transport manager to shutdown transport.
+ */
+static pj_status_t tcp_shutdown(pjsip_transport *transport)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*)transport;
+    
+    /* Stop keep-alive timer. */
+    if (tcp->ka_timer.id) {
+	pjsip_endpt_cancel_timer(tcp->base.endpt, &tcp->ka_timer);
+	tcp->ka_timer.id = PJ_FALSE;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* 
+ * Callback from ioqueue that an incoming data is received from the socket.
+ */
+static pj_bool_t on_data_read(pj_activesock_t *asock,
+			      void *data,
+			      pj_size_t size,
+			      pj_status_t status,
+			      pj_size_t *remainder)
+{
+    enum { MAX_IMMEDIATE_PACKET = 10 };
+    struct tcp_transport *tcp;
+    pjsip_rx_data *rdata;
+
+    PJ_UNUSED_ARG(data);
+
+    tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock);
+    rdata = &tcp->rdata;
+
+    /* Don't do anything if transport is closing. */
+    if (tcp->is_closing) {
+	tcp->is_closing++;
+	return PJ_FALSE;
+    }
+
+    /* Houston, we have packet! Report the packet to transport manager
+     * to be parsed.
+     */
+    if (status == PJ_SUCCESS) {
+	pj_size_t size_eaten;
+
+	/* Mark this as an activity */
+	pj_gettimeofday(&tcp->last_activity);
+
+	pj_assert((void*)rdata->pkt_info.packet == data);
+
+	/* Init pkt_info part. */
+	rdata->pkt_info.len = size;
+	rdata->pkt_info.zero = 0;
+	pj_gettimeofday(&rdata->pkt_info.timestamp);
+
+	/* Report to transport manager.
+	 * The transport manager will tell us how many bytes of the packet
+	 * have been processed (as valid SIP message).
+	 */
+	size_eaten = 
+	    pjsip_tpmgr_receive_packet(rdata->tp_info.transport->tpmgr, 
+				       rdata);
+
+	pj_assert(size_eaten <= (pj_size_t)rdata->pkt_info.len);
+
+	/* Move unprocessed data to the front of the buffer */
+	*remainder = size - size_eaten;
+	if (*remainder > 0 && *remainder != size) {
+	    pj_memmove(rdata->pkt_info.packet,
+		       rdata->pkt_info.packet + size_eaten,
+		       *remainder);
+	}
+
+    } else {
+
+	/* Transport is closed */
+	PJ_LOG(4,(tcp->base.obj_name, "TCP connection closed"));
+	
+	tcp_init_shutdown(tcp, status);
+
+	return PJ_FALSE;
+
+    }
+
+    /* Reset pool. */
+    pj_pool_reset(rdata->tp_info.pool);
+
+    return PJ_TRUE;
+}
+
+
+/* 
+ * Callback from ioqueue when asynchronous connect() operation completes.
+ */
+static pj_bool_t on_connect_complete(pj_activesock_t *asock,
+				     pj_status_t status)
+{
+    struct tcp_transport *tcp;
+    pj_sockaddr addr;
+    int addrlen;
+    pjsip_tp_state_callback state_cb;
+
+    tcp = (struct tcp_transport*) pj_activesock_get_user_data(asock);
+
+    /* Mark that pending connect() operation has completed. */
+    tcp->has_pending_connect = PJ_FALSE;
+
+    /* Check connect() status */
+    if (status != PJ_SUCCESS) {
+
+	tcp_perror(tcp->base.obj_name, "TCP connect() error", status);
+
+	/* Cancel all delayed transmits */
+	while (!pj_list_empty(&tcp->delayed_list)) {
+	    struct delayed_tdata *pending_tx;
+	    pj_ioqueue_op_key_t *op_key;
+
+	    pending_tx = tcp->delayed_list.next;
+	    pj_list_erase(pending_tx);
+
+	    op_key = (pj_ioqueue_op_key_t*)pending_tx->tdata_op_key;
+
+	    on_data_sent(tcp->asock, op_key, -status);
+	}
+
+	tcp_init_shutdown(tcp, status);
+	return PJ_FALSE;
+    }
+
+    PJ_LOG(4,(tcp->base.obj_name, 
+	      "TCP transport %.*s:%d is connected to %.*s:%d",
+	      (int)tcp->base.local_name.host.slen,
+	      tcp->base.local_name.host.ptr,
+	      tcp->base.local_name.port,
+	      (int)tcp->base.remote_name.host.slen,
+	      tcp->base.remote_name.host.ptr,
+	      tcp->base.remote_name.port));
+
+
+    /* Update (again) local address, just in case local address currently
+     * set is different now that the socket is connected (could happen
+     * on some systems, like old Win32 probably?).
+     */
+    addrlen = sizeof(addr);
+    if (pj_sock_getsockname(tcp->sock, &addr, &addrlen)==PJ_SUCCESS) {
+	pj_sockaddr *tp_addr = &tcp->base.local_addr;
+
+	if (pj_sockaddr_has_addr(&addr) &&
+	    pj_sockaddr_cmp(&addr, tp_addr) != 0)
+	{
+	    pj_sockaddr_cp(tp_addr, &addr);
+	    sockaddr_to_host_port(tcp->base.pool, &tcp->base.local_name,
+				  tp_addr);
+	}
+    }
+
+    /* Start pending read */
+    status = tcp_start_read(tcp);
+    if (status != PJ_SUCCESS) {
+	tcp_init_shutdown(tcp, status);
+	return PJ_FALSE;
+    }
+
+    /* Notify application of transport state connected */
+    state_cb = pjsip_tpmgr_get_state_cb(tcp->base.tpmgr);
+    if (state_cb) {
+	pjsip_transport_state_info state_info;
+    
+	pj_bzero(&state_info, sizeof(state_info));
+	(*state_cb)(&tcp->base, PJSIP_TP_STATE_CONNECTED, &state_info);
+    }
+
+    /* Flush all pending send operations */
+    tcp_flush_pending_tx(tcp);
+
+    /* Start keep-alive timer */
+    if (PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+	pj_time_val delay = { PJSIP_TCP_KEEP_ALIVE_INTERVAL, 0 };
+	pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, 
+				   &delay);
+	tcp->ka_timer.id = PJ_TRUE;
+	pj_gettimeofday(&tcp->last_activity);
+    }
+
+    return PJ_TRUE;
+}
+
+/* Transport keep-alive timer callback */
+static void tcp_keep_alive_timer(pj_timer_heap_t *th, pj_timer_entry *e)
+{
+    struct tcp_transport *tcp = (struct tcp_transport*) e->user_data;
+    pj_time_val delay;
+    pj_time_val now;
+    pj_ssize_t size;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(th);
+
+    tcp->ka_timer.id = PJ_TRUE;
+
+    pj_gettimeofday(&now);
+    PJ_TIME_VAL_SUB(now, tcp->last_activity);
+
+    if (now.sec > 0 && now.sec < PJSIP_TCP_KEEP_ALIVE_INTERVAL) {
+	/* There has been activity, so don't send keep-alive */
+	delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL - now.sec;
+	delay.msec = 0;
+
+	pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, 
+				   &delay);
+	tcp->ka_timer.id = PJ_TRUE;
+	return;
+    }
+
+    PJ_LOG(5,(tcp->base.obj_name, "Sending %d byte(s) keep-alive to %.*s:%d", 
+	      (int)tcp->ka_pkt.slen, (int)tcp->base.remote_name.host.slen,
+	      tcp->base.remote_name.host.ptr,
+	      tcp->base.remote_name.port));
+
+    /* Send the data */
+    size = tcp->ka_pkt.slen;
+    status = pj_activesock_send(tcp->asock, &tcp->ka_op_key.key,
+				tcp->ka_pkt.ptr, &size, 0);
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	tcp_perror(tcp->base.obj_name, 
+		   "Error sending keep-alive packet", status);
+	tcp_init_shutdown(tcp, status);
+	return;
+    }
+
+    /* Register next keep-alive */
+    delay.sec = PJSIP_TCP_KEEP_ALIVE_INTERVAL;
+    delay.msec = 0;
+
+    pjsip_endpt_schedule_timer(tcp->base.endpt, &tcp->ka_timer, 
+			       &delay);
+    tcp->ka_timer.id = PJ_TRUE;
+}
+
+
+#endif	/* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/.svn/pristine/21/215418964892f1471a05aed3ef4f15999e03e2b8.svn-base b/jni/pjproject-android/.svn/pristine/21/215418964892f1471a05aed3ef4f15999e03e2b8.svn-base
new file mode 100644
index 0000000..3d2ebc4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/215418964892f1471a05aed3ef4f15999e03e2b8.svn-base
@@ -0,0 +1,226 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJSUA_APP_COMMON_H__
+#define __PJSUA_APP_COMMON_H__
+
+#include <pjsua-lib/pjsua.h>
+
+PJ_BEGIN_DECL
+
+#define current_acc	pjsua_acc_get_default()
+
+#define PJSUA_APP_NO_LIMIT_DURATION	(int)0x7FFFFFFF
+#define PJSUA_APP_MAX_AVI		4
+#define PJSUA_APP_NO_NB			-2
+
+typedef struct input_result
+{
+    int	  nb_result;
+    char *uri_result;
+} input_result;
+
+/* Call specific data */
+typedef struct app_call_data
+{
+    pj_timer_entry	    timer;
+    pj_bool_t		    ringback_on;
+    pj_bool_t		    ring_on;
+} app_call_data;
+
+/* Video settings */
+typedef struct app_vid
+{
+    unsigned		    vid_cnt;
+    int			    vcapture_dev;
+    int			    vrender_dev;
+    pj_bool_t		    in_auto_show;
+    pj_bool_t		    out_auto_transmit;
+} app_vid;
+
+/* Enumeration of CLI frontends */
+typedef enum {
+    CLI_FE_CONSOLE	    = 1,
+    CLI_FE_TELNET	    = 2
+} CLI_FE;
+
+/** CLI config **/
+typedef struct cli_cfg_t
+{
+    /** Bitmask of CLI_FE **/
+    int			    cli_fe;
+    pj_cli_cfg		    cfg;
+    pj_cli_telnet_cfg	    telnet_cfg;
+    pj_cli_console_cfg	    console_cfg;
+} cli_cfg_t;
+
+/* Pjsua application data */
+typedef struct pjsua_app_config
+{
+    pjsua_config	    cfg;
+    pjsua_logging_config    log_cfg;
+    pjsua_media_config	    media_cfg;
+    pj_bool_t		    no_refersub;
+    pj_bool_t		    ipv6;
+    pj_bool_t		    enable_qos;
+    pj_bool_t		    no_tcp;
+    pj_bool_t		    no_udp;
+    pj_bool_t		    use_tls;
+    pjsua_transport_config  udp_cfg;
+    pjsua_transport_config  rtp_cfg;
+    pjsip_redirect_op	    redir_op;
+
+    unsigned		    acc_cnt;
+    pjsua_acc_config	    acc_cfg[PJSUA_MAX_ACC];
+
+    unsigned		    buddy_cnt;
+    pjsua_buddy_config	    buddy_cfg[PJSUA_MAX_BUDDIES];
+
+    app_call_data	    call_data[PJSUA_MAX_CALLS];
+
+    pj_pool_t		   *pool;
+    /* Compatibility with older pjsua */
+
+    unsigned		    codec_cnt;
+    pj_str_t		    codec_arg[32];
+    unsigned		    codec_dis_cnt;
+    pj_str_t                codec_dis[32];
+    pj_bool_t		    null_audio;
+    unsigned		    wav_count;
+    pj_str_t		    wav_files[32];
+    unsigned		    tone_count;
+    pjmedia_tone_desc	    tones[32];
+    pjsua_conf_port_id	    tone_slots[32];
+    pjsua_player_id	    wav_id;
+    pjsua_conf_port_id	    wav_port;
+    pj_bool_t		    auto_play;
+    pj_bool_t		    auto_play_hangup;
+    pj_timer_entry	    auto_hangup_timer;
+    pj_bool_t		    auto_loop;
+    pj_bool_t		    auto_conf;
+    pj_str_t		    rec_file;
+    pj_bool_t		    auto_rec;
+    pjsua_recorder_id	    rec_id;
+    pjsua_conf_port_id	    rec_port;
+    unsigned		    auto_answer;
+    unsigned		    duration;
+
+#ifdef STEREO_DEMO
+    pjmedia_snd_port	   *snd;
+    pjmedia_port	   *sc, *sc_ch1;
+    pjsua_conf_port_id	    sc_ch1_slot;
+#endif
+
+    float		    mic_level,
+			    speaker_level;
+
+    int			    capture_dev, playback_dev;
+    unsigned		    capture_lat, playback_lat;
+
+    pj_bool_t		    no_tones;
+    int			    ringback_slot;
+    int			    ringback_cnt;
+    pjmedia_port	   *ringback_port;
+    int			    ring_slot;
+    int			    ring_cnt;
+    pjmedia_port	   *ring_port;
+
+    app_vid		    vid;
+    unsigned		    aud_cnt;
+
+    /* AVI to play */
+    unsigned                avi_cnt;
+    struct {
+	pj_str_t		path;
+	pjmedia_vid_dev_index	dev_id;
+	pjsua_conf_port_id	slot;
+    } avi[PJSUA_APP_MAX_AVI];
+    pj_bool_t               avi_auto_play;
+    int			    avi_def_idx;
+
+    /* CLI setting */
+    pj_bool_t		    use_cli;
+    cli_cfg_t		    cli_cfg;
+} pjsua_app_config;
+
+/** Extern variable declaration **/
+extern pjsua_call_id	    current_call;
+extern pjsua_app_config	    app_config;
+extern int		    stdout_refresh;
+extern pj_bool_t	    stdout_refresh_quit;
+extern pjsua_call_setting   call_opt;
+extern pjsua_msg_data	    msg_data;
+extern pj_bool_t	    app_running;
+
+int my_atoi(const char *cs);
+pj_bool_t find_next_call();
+pj_bool_t find_prev_call();
+void send_request(char *cstr_method, const pj_str_t *dst_uri);
+void log_call_dump(int call_id);
+int write_settings(pjsua_app_config *cfg, char *buf, pj_size_t max);
+void app_config_init_video(pjsua_acc_config *acc_cfg);
+void arrange_window(pjsua_vid_win_id wid);
+
+/** Defined in pjsua_cli_cmd.c **/
+pj_bool_t is_cli_inited();
+
+/** Defined in pjsua_config.c **/
+/** This is to load the configuration **/
+pj_status_t load_config(int argc, char **argv, pj_str_t *uri_arg);
+
+/** Pjsua app callback **/
+/** This callback is called when CLI is started. **/
+void cli_on_started(pj_status_t status);
+
+/** This callback is called when "shutdown"/"restart" command is invoked **/
+void cli_on_stopped(pj_bool_t restart, int argc, char **argv);
+
+/** This callback is called when "quit"/"restart" command is invoked **/
+void legacy_on_stopped(pj_bool_t restart);
+
+/** Pjsua cli method **/
+pj_status_t cli_init();
+pj_status_t cli_main(pj_bool_t wait_telnet_cli);
+void cli_destroy();
+void cli_get_info(char *info, pj_size_t size); 
+
+/** Legacy method **/
+void legacy_main();
+
+#if PJSUA_HAS_VIDEO
+void vid_print_dev(int id, const pjmedia_vid_dev_info *vdi, const char *title);
+void vid_list_devs();
+void app_config_show_video(int acc_id, const pjsua_acc_config *acc_cfg);
+#endif
+
+#ifdef HAVE_MULTIPART_TEST
+    /*
+    * Enable multipart in msg_data and add a dummy body into the
+    * multipart bodies.
+    */
+    void add_multipart(pjsua_msg_data *msg_data);
+#  define TEST_MULTIPART(msg_data)	add_multipart(msg_data)
+#else
+#  define TEST_MULTIPART(msg_data)
+#endif
+
+
+PJ_END_DECL
+    
+#endif	/* __PJSUA_APP_COMMON_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base b/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base
new file mode 100644
index 0000000..282c35d
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21809bf569e567c198b60ff3d9126c1a1fe704ef.svn-base
@@ -0,0 +1,5363 @@
+//------------------------------------------------------------------------------

+// File: AMFilter.cpp

+//

+// Desc: DirectShow base classes - implements class hierarchy for streams

+//       architecture.

+//

+// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.

+//------------------------------------------------------------------------------

+

+

+//=====================================================================

+//=====================================================================

+// The following classes are declared in this header:

+//

+//

+// CBaseMediaFilter            Basic IMediaFilter support (abstract class)

+// CBaseFilter                 Support for IBaseFilter (incl. IMediaFilter)

+// CEnumPins                   Enumerate input and output pins

+// CEnumMediaTypes             Enumerate the preferred pin formats

+// CBasePin                    Abstract base class for IPin interface

+//    CBaseOutputPin           Adds data provider member functions

+//    CBaseInputPin            Implements IMemInputPin interface

+// CMediaSample                Basic transport unit for IMemInputPin

+// CBaseAllocator              General list guff for most allocators

+//    CMemAllocator            Implements memory buffer allocation

+//

+//=====================================================================

+//=====================================================================

+

+#include <pjmedia-videodev/config.h>

+

+#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0

+

+#include <streams.h>

+#include <strsafe.h>

+

+#ifdef DXMPERF

+#include "dxmperf.h"

+#endif // DXMPERF

+

+

+//=====================================================================

+// Helpers

+//=====================================================================

+STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)

+{

+    return CoCreateInstance(CLSID_MemoryAllocator,

+                            0,

+                            CLSCTX_INPROC_SERVER,

+                            IID_IMemAllocator,

+                            (void **)ppAllocator);

+}

+

+//  Put this one here rather than in ctlutil.cpp to avoid linking

+//  anything brought in by ctlutil.cpp

+STDAPI CreatePosPassThru(

+    __in_opt LPUNKNOWN pAgg,

+    BOOL bRenderer,

+    IPin *pPin,

+    __deref_out IUnknown **ppPassThru

+)

+{

+    *ppPassThru = NULL;

+    IUnknown *pUnkSeek;

+    HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,

+                                  pAgg,

+                                  CLSCTX_INPROC_SERVER,

+                                  IID_IUnknown,

+                                  (void **)&pUnkSeek

+                                 );

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    ISeekingPassThru *pPassThru;

+    hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);

+    if (FAILED(hr)) {

+        pUnkSeek->Release();

+        return hr;

+    }

+    hr = pPassThru->Init(bRenderer, pPin);

+    pPassThru->Release();

+    if (FAILED(hr)) {

+        pUnkSeek->Release();

+        return hr;

+    }

+    *ppPassThru = pUnkSeek;

+    return S_OK;

+}

+

+

+

+#define CONNECT_TRACE_LEVEL 3

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseMediaFilter

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor */

+

+CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,

+                   __inout_opt LPUNKNOWN    pUnk,

+                   __in CCritSec *pLock,

+                   REFCLSID clsid) :

+    CUnknown(pName, pUnk),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL)

+{

+}

+

+

+/* Destructor */

+

+CBaseMediaFilter::~CBaseMediaFilter()

+{

+    // must be stopped, but can't call Stop here since

+    // our critsec has been destroyed.

+

+    /* Release any clock we were using */

+

+    if (m_pClock) {

+        m_pClock->Release();

+        m_pClock = NULL;

+    }

+}

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP

+CBaseMediaFilter::NonDelegatingQueryInterface(

+    REFIID riid,

+    __deref_out void ** ppv)

+{

+    if (riid == IID_IMediaFilter) {

+        return GetInterface((IMediaFilter *) this, ppv);

+    } else if (riid == IID_IPersist) {

+        return GetInterface((IPersist *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+/* Return the filter's clsid */

+STDMETHODIMP

+CBaseMediaFilter::GetClassID(__out CLSID *pClsID)

+{

+    CheckPointer(pClsID,E_POINTER);

+    ValidateReadWritePtr(pClsID,sizeof(CLSID));

+    *pClsID = m_clsid;

+    return NOERROR;

+}

+

+/* Override this if your state changes are not done synchronously */

+

+STDMETHODIMP

+CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)

+{

+    UNREFERENCED_PARAMETER(dwMSecs);

+    CheckPointer(State,E_POINTER);

+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));

+

+    *State = m_State;

+    return S_OK;

+}

+

+

+/* Set the clock we will use for synchronisation */

+

+STDMETHODIMP

+CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // Ensure the new one does not go away - even if the same as the old

+    if (pClock) {

+        pClock->AddRef();

+    }

+

+    // if we have a clock, release it

+    if (m_pClock) {

+        m_pClock->Release();

+    }

+

+    // Set the new reference clock (might be NULL)

+    // Should we query it to ensure it is a clock?  Consider for a debug build.

+    m_pClock = pClock;

+

+    return NOERROR;

+}

+

+/* Return the clock we are using for synchronisation */

+STDMETHODIMP

+CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)

+{

+    CheckPointer(pClock,E_POINTER);

+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pClock) {

+        // returning an interface... addref it...

+        m_pClock->AddRef();

+    }

+    *pClock = (IReferenceClock*)m_pClock;

+    return NOERROR;

+}

+

+

+/* Put the filter into a stopped state */

+

+STDMETHODIMP

+CBaseMediaFilter::Stop()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    m_State = State_Stopped;

+    return S_OK;

+}

+

+

+/* Put the filter into a paused state */

+

+STDMETHODIMP

+CBaseMediaFilter::Pause()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    m_State = State_Paused;

+    return S_OK;

+}

+

+

+// Put the filter into a running state.

+

+// The time parameter is the offset to be added to the samples'

+// stream time to get the reference time at which they should be presented.

+//

+// you can either add these two and compare it against the reference clock,

+// or you can call CBaseMediaFilter::StreamTime and compare that against

+// the sample timestamp.

+

+STDMETHODIMP

+CBaseMediaFilter::Run(REFERENCE_TIME tStart)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // remember the stream time offset

+    m_tStart = tStart;

+

+    if (m_State == State_Stopped){

+        HRESULT hr = Pause();

+

+        if (FAILED(hr)) {

+            return hr;

+        }

+    }

+    m_State = State_Running;

+    return S_OK;

+}

+

+

+//

+// return the current stream time - samples with start timestamps of this

+// time or before should be rendered by now

+HRESULT

+CBaseMediaFilter::StreamTime(CRefTime& rtStream)

+{

+    // Caller must lock for synchronization

+    // We can't grab the filter lock because we want to be able to call

+    // this from worker threads without deadlocking

+

+    if (m_pClock == NULL) {

+        return VFW_E_NO_CLOCK;

+    }

+

+    // get the current reference time

+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    // subtract the stream offset to get stream time

+    rtStream -= m_tStart;

+

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseFilter

+//=====================================================================

+//=====================================================================

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,

+                                                      __deref_out void **ppv)

+{

+    /* Do we have this interface */

+

+    if (riid == IID_IBaseFilter) {

+        return GetInterface((IBaseFilter *) this, ppv);

+    } else if (riid == IID_IMediaFilter) {

+        return GetInterface((IMediaFilter *) this, ppv);

+    } else if (riid == IID_IPersist) {

+        return GetInterface((IPersist *) this, ppv);

+    } else if (riid == IID_IAMovieSetup) {

+        return GetInterface((IAMovieSetup *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+#ifdef DEBUG

+STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()

+{

+    if (m_cRef == 1) {

+        KASSERT(m_pGraph == NULL);

+    }

+    return CUnknown::NonDelegatingRelease();

+}

+#endif

+

+

+/* Constructor */

+

+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,

+             __inout_opt LPUNKNOWN  pUnk,

+             __in CCritSec   *pLock,

+             REFCLSID   clsid) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+}

+

+/* Passes in a redundant HRESULT argument */

+

+CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,

+                         __in_opt LPUNKNOWN  pUnk,

+                         __in CCritSec  *pLock,

+                         REFCLSID   clsid,

+                         __inout HRESULT   *phr) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+    UNREFERENCED_PARAMETER(phr);

+}

+

+#ifdef UNICODE

+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,

+             __in_opt LPUNKNOWN  pUnk,

+             __in CCritSec   *pLock,

+             REFCLSID   clsid) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+}

+CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,

+                         __in_opt LPUNKNOWN  pUnk,

+                         __in CCritSec  *pLock,

+                         REFCLSID   clsid,

+                         __inout HRESULT   *phr) :

+    CUnknown( pName, pUnk ),

+    m_pLock(pLock),

+    m_clsid(clsid),

+    m_State(State_Stopped),

+    m_pClock(NULL),

+    m_pGraph(NULL),

+    m_pSink(NULL),

+    m_pName(NULL),

+    m_PinVersion(1)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    ASSERT(pLock != NULL);

+    UNREFERENCED_PARAMETER(phr);

+}

+#endif

+

+/* Destructor */

+

+CBaseFilter::~CBaseFilter()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );

+#endif // DXMPERF

+

+    // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink

+    // When we did we had the circular reference problem.  Nothing would go away.

+

+    delete[] m_pName;

+

+    // must be stopped, but can't call Stop here since

+    // our critsec has been destroyed.

+

+    /* Release any clock we were using */

+    if (m_pClock) {

+        m_pClock->Release();

+        m_pClock = NULL;

+    }

+}

+

+/* Return the filter's clsid */

+STDMETHODIMP

+CBaseFilter::GetClassID(__out CLSID *pClsID)

+{

+    CheckPointer(pClsID,E_POINTER);

+    ValidateReadWritePtr(pClsID,sizeof(CLSID));

+    *pClsID = m_clsid;

+    return NOERROR;

+}

+

+/* Override this if your state changes are not done synchronously */

+STDMETHODIMP

+CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)

+{

+    UNREFERENCED_PARAMETER(dwMSecs);

+    CheckPointer(State,E_POINTER);

+    ValidateReadWritePtr(State,sizeof(FILTER_STATE));

+

+    *State = m_State;

+    return S_OK;

+}

+

+

+/* Set the clock we will use for synchronisation */

+

+STDMETHODIMP

+CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // Ensure the new one does not go away - even if the same as the old

+    if (pClock) {

+        pClock->AddRef();

+    }

+

+    // if we have a clock, release it

+    if (m_pClock) {

+        m_pClock->Release();

+    }

+

+    // Set the new reference clock (might be NULL)

+    // Should we query it to ensure it is a clock?  Consider for a debug build.

+    m_pClock = pClock;

+

+    return NOERROR;

+}

+

+/* Return the clock we are using for synchronisation */

+STDMETHODIMP

+CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)

+{

+    CheckPointer(pClock,E_POINTER);

+    ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pClock) {

+        // returning an interface... addref it...

+        m_pClock->AddRef();

+    }

+    *pClock = (IReferenceClock*)m_pClock;

+    return NOERROR;

+}

+

+

+

+// override CBaseMediaFilter Stop method, to deactivate any pins this

+// filter has.

+STDMETHODIMP

+CBaseFilter::Stop()

+{

+    CAutoLock cObjectLock(m_pLock);

+    HRESULT hr = NOERROR;

+

+    // notify all pins of the state change

+    if (m_State != State_Stopped) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins worrying

+            // about this state themselves. We ignore the return code to make

+            // sure everyone is inactivated regardless. The base input pin

+            // class can return an error if it has no allocator but Stop can

+            // be used to resync the graph state after something has gone bad

+

+            if (pPin->IsConnected()) {

+                HRESULT hrTmp = pPin->Inactive();

+                if (FAILED(hrTmp) && SUCCEEDED(hr)) {

+                    hr = hrTmp;

+                }

+            }

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );

+#endif // DXMPERF

+

+    m_State = State_Stopped;

+    return hr;

+}

+

+

+// override CBaseMediaFilter Pause method to activate any pins

+// this filter has (also called from Run)

+

+STDMETHODIMP

+CBaseFilter::Pause()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // notify all pins of the change to active state

+    if (m_State == State_Stopped) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins

+            // worrying about this state themselves

+

+            if (pPin->IsConnected()) {

+                HRESULT hr = pPin->Active();

+                if (FAILED(hr)) {

+                    return hr;

+                }

+            }

+        }

+    }

+

+

+#ifdef DXMPERF

+    PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );

+#endif // DXMPERF

+

+    m_State = State_Paused;

+    return S_OK;

+}

+

+// Put the filter into a running state.

+

+// The time parameter is the offset to be added to the samples'

+// stream time to get the reference time at which they should be presented.

+//

+// you can either add these two and compare it against the reference clock,

+// or you can call CBaseFilter::StreamTime and compare that against

+// the sample timestamp.

+

+STDMETHODIMP

+CBaseFilter::Run(REFERENCE_TIME tStart)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // remember the stream time offset

+    m_tStart = tStart;

+

+    if (m_State == State_Stopped){

+    HRESULT hr = Pause();

+

+    if (FAILED(hr)) {

+        return hr;

+    }

+    }

+    // notify all pins of the change to active state

+    if (m_State != State_Running) {

+        int cPins = GetPinCount();

+        for (int c = 0; c < cPins; c++) {

+

+            CBasePin *pPin = GetPin(c);

+            if (NULL == pPin) {

+                break;

+            }

+

+            // Disconnected pins are not activated - this saves pins

+            // worrying about this state themselves

+

+            if (pPin->IsConnected()) {

+                HRESULT hr = pPin->Run(tStart);

+                if (FAILED(hr)) {

+                    return hr;

+                }

+            }

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );

+#endif // DXMPERF

+

+    m_State = State_Running;

+    return S_OK;

+}

+

+//

+// return the current stream time - samples with start timestamps of this

+// time or before should be rendered by now

+HRESULT

+CBaseFilter::StreamTime(CRefTime& rtStream)

+{

+    // Caller must lock for synchronization

+    // We can't grab the filter lock because we want to be able to call

+    // this from worker threads without deadlocking

+

+    if (m_pClock == NULL) {

+        return VFW_E_NO_CLOCK;

+    }

+

+    // get the current reference time

+    HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    // subtract the stream offset to get stream time

+    rtStream -= m_tStart;

+

+    return S_OK;

+}

+

+

+/* Create an enumerator for the pins attached to this filter */

+

+STDMETHODIMP

+CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));

+

+    /* Create a new ref counted enumerator */

+

+    *ppEnum = new CEnumPins(this,

+                        NULL);

+

+    return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;

+}

+

+

+// default behaviour of FindPin is to assume pins are named

+// by their pin names

+STDMETHODIMP

+CBaseFilter::FindPin(

+    LPCWSTR Id,

+    __deref_out IPin ** ppPin

+)

+{

+    CheckPointer(ppPin,E_POINTER);

+    ValidateReadWritePtr(ppPin,sizeof(IPin *));

+

+    //  We're going to search the pin list so maintain integrity

+    CAutoLock lck(m_pLock);

+    int iCount = GetPinCount();

+    for (int i = 0; i < iCount; i++) {

+        CBasePin *pPin = GetPin(i);

+        if (NULL == pPin) {

+            break;

+        }

+

+        if (0 == lstrcmpW(pPin->Name(), Id)) {

+            //  Found one that matches

+            //

+            //  AddRef() and return it

+            *ppPin = pPin;

+            pPin->AddRef();

+            return S_OK;

+        }

+    }

+    *ppPin = NULL;

+    return VFW_E_NOT_FOUND;

+}

+

+/* Return information about this filter */

+

+STDMETHODIMP

+CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)

+{

+    CheckPointer(pInfo,E_POINTER);

+    ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));

+

+    if (m_pName) {

+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);

+    } else {

+        pInfo->achName[0] = L'\0';

+    }

+    pInfo->pGraph = m_pGraph;

+    if (m_pGraph)

+        m_pGraph->AddRef();

+    return NOERROR;

+}

+

+

+/* Provide the filter with a filter graph */

+

+STDMETHODIMP

+CBaseFilter::JoinFilterGraph(

+    __inout_opt IFilterGraph * pGraph,

+    __in_opt LPCWSTR pName)

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)

+

+    m_pGraph = pGraph;

+    if (m_pGraph) {

+        HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,

+                        (void**) &m_pSink);

+        if (FAILED(hr)) {

+            ASSERT(m_pSink == NULL);

+        }

+        else m_pSink->Release();        // we do NOT keep a reference on it.

+    } else {

+        // if graph pointer is null, then we should

+        // also release the IMediaEventSink on the same object - we don't

+        // refcount it, so just set it to null

+        m_pSink = NULL;

+    }

+

+

+    if (m_pName) {

+        delete[] m_pName;

+        m_pName = NULL;

+    }

+

+    if (pName) {

+        size_t namelen;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);

+        if (FAILED(hr)) {

+            return hr;

+        }

+        m_pName = new WCHAR[namelen + 1];

+        if (m_pName) {

+            (void)StringCchCopyW(m_pName, namelen + 1, pName);

+        } else {

+            return E_OUTOFMEMORY;

+        }

+    }

+

+#ifdef DXMPERF

+    PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+

+// return a Vendor information string. Optional - may return E_NOTIMPL.

+// memory returned should be freed using CoTaskMemFree

+// default implementation returns E_NOTIMPL

+STDMETHODIMP

+CBaseFilter::QueryVendorInfo(

+    __deref_out LPWSTR* pVendorInfo)

+{

+    UNREFERENCED_PARAMETER(pVendorInfo);

+    return E_NOTIMPL;

+}

+

+

+// send an event notification to the filter graph if we know about it.

+// returns S_OK if delivered, S_FALSE if the filter graph does not sink

+// events, or an error otherwise.

+HRESULT

+CBaseFilter::NotifyEvent(

+    long EventCode,

+    LONG_PTR EventParam1,

+    LONG_PTR EventParam2)

+{

+    // Snapshot so we don't have to lock up

+    IMediaEventSink *pSink = m_pSink;

+    if (pSink) {

+        if (EC_COMPLETE == EventCode) {

+            EventParam2 = (LONG_PTR)(IBaseFilter*)this;

+        }

+

+        return pSink->Notify(EventCode, EventParam1, EventParam2);

+    } else {

+        return E_NOTIMPL;

+    }

+}

+

+// Request reconnect

+// pPin is the pin to reconnect

+// pmt is the type to reconnect with - can be NULL

+// Calls ReconnectEx on the filter graph

+HRESULT

+CBaseFilter::ReconnectPin(

+    IPin *pPin,

+    __in_opt AM_MEDIA_TYPE const *pmt

+)

+{

+    IFilterGraph2 *pGraph2;

+    if (m_pGraph != NULL) {

+        HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);

+        if (SUCCEEDED(hr)) {

+            hr = pGraph2->ReconnectEx(pPin, pmt);

+            pGraph2->Release();

+            return hr;

+        } else {

+            return m_pGraph->Reconnect(pPin);

+        }

+    } else {

+        return E_NOINTERFACE;

+    }

+}

+

+

+

+/* This is the same idea as the media type version does for type enumeration

+   on pins but for the list of pins available. So if the list of pins you

+   provide changes dynamically then either override this virtual function

+   to provide the version number, or more simply call IncrementPinVersion */

+

+LONG CBaseFilter::GetPinVersion()

+{

+    return m_PinVersion;

+}

+

+

+/* Increment the current pin version cookie */

+

+void CBaseFilter::IncrementPinVersion()

+{

+    InterlockedIncrement(&m_PinVersion);

+}

+

+/* register filter */

+

+STDMETHODIMP CBaseFilter::Register()

+{

+    // get setup data, if it exists

+    //

+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();

+

+    // check we've got data

+    //

+    if( NULL == psetupdata ) return S_FALSE;

+

+    // init is ref counted so call just in case

+    // we're being called cold.

+    //

+    HRESULT hr = CoInitialize( (LPVOID)NULL );

+    ASSERT( SUCCEEDED(hr) );

+

+    // get hold of IFilterMapper

+    //

+    IFilterMapper *pIFM;

+    hr = CoCreateInstance( CLSID_FilterMapper

+                             , NULL

+                             , CLSCTX_INPROC_SERVER

+                             , IID_IFilterMapper

+                             , (void **)&pIFM       );

+    if( SUCCEEDED(hr) )

+    {

+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );

+        pIFM->Release();

+    }

+

+    // and clear up

+    //

+    CoFreeUnusedLibraries();

+    CoUninitialize();

+

+    return NOERROR;

+}

+

+

+/* unregister filter */

+

+STDMETHODIMP CBaseFilter::Unregister()

+{

+    // get setup data, if it exists

+    //

+    LPAMOVIESETUP_FILTER psetupdata = GetSetupData();

+

+    // check we've got data

+    //

+    if( NULL == psetupdata ) return S_FALSE;

+

+    // OLE init is ref counted so call

+    // just in case we're being called cold.

+    //

+    HRESULT hr = CoInitialize( (LPVOID)NULL );

+    ASSERT( SUCCEEDED(hr) );

+

+    // get hold of IFilterMapper

+    //

+    IFilterMapper *pIFM;

+    hr = CoCreateInstance( CLSID_FilterMapper

+                             , NULL

+                             , CLSCTX_INPROC_SERVER

+                             , IID_IFilterMapper

+                             , (void **)&pIFM       );

+    if( SUCCEEDED(hr) )

+    {

+        hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );

+

+        // release interface

+        //

+        pIFM->Release();

+    }

+

+    // clear up

+    //

+    CoFreeUnusedLibraries();

+    CoUninitialize();

+

+    // handle one acceptable "error" - that

+    // of filter not being registered!

+    // (couldn't find a suitable #define'd

+    // name for the error!)

+    //

+    if( 0x80070002 == hr)

+      return NOERROR;

+    else

+      return hr;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CEnumPins

+//=====================================================================

+//=====================================================================

+

+

+CEnumPins::CEnumPins(__in CBaseFilter *pFilter,

+                     __in_opt CEnumPins *pEnumPins) :

+    m_Position(0),

+    m_PinCount(0),

+    m_pFilter(pFilter),

+    m_cRef(1),               // Already ref counted

+    m_PinCache(NAME("Pin Cache"))

+{

+

+#ifdef DEBUG

+    m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);

+#endif

+

+    /* We must be owned by a filter derived from CBaseFilter */

+

+    ASSERT(pFilter != NULL);

+

+    /* Hold a reference count on our filter */

+    m_pFilter->AddRef();

+

+    /* Are we creating a new enumerator */

+

+    if (pEnumPins == NULL) {

+        m_Version = m_pFilter->GetPinVersion();

+        m_PinCount = m_pFilter->GetPinCount();

+    } else {

+        ASSERT(m_Position <= m_PinCount);

+        m_Position = pEnumPins->m_Position;

+        m_PinCount = pEnumPins->m_PinCount;

+        m_Version = pEnumPins->m_Version;

+        m_PinCache.AddTail(&(pEnumPins->m_PinCache));

+    }

+}

+

+

+/* Destructor releases the reference count on our filter NOTE since we hold

+   a reference count on the filter who created us we know it is safe to

+   release it, no access can be made to it afterwards though as we have just

+   caused the last reference count to go and the object to be deleted */

+

+CEnumPins::~CEnumPins()

+{

+    m_pFilter->Release();

+

+#ifdef DEBUG

+    DbgRegisterObjectDestruction(m_dwCookie);

+#endif

+}

+

+

+/* Override this to say what interfaces we support where */

+

+STDMETHODIMP

+CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    CheckPointer(ppv, E_POINTER);

+

+    /* Do we have this interface */

+

+    if (riid == IID_IEnumPins || riid == IID_IUnknown) {

+        return GetInterface((IEnumPins *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CEnumPins::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+STDMETHODIMP_(ULONG)

+CEnumPins::Release()

+{

+    ULONG cRef = InterlockedDecrement(&m_cRef);

+    if (cRef == 0) {

+        delete this;

+    }

+    return cRef;

+}

+

+/* One of an enumerator's basic member functions allows us to create a cloned

+   interface that initially has the same state. Since we are taking a snapshot

+   of an object (current position and all) we must lock access at the start */

+

+STDMETHODIMP 

+CEnumPins::Clone(__deref_out IEnumPins **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));

+    HRESULT hr = NOERROR;

+

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        *ppEnum = NULL;

+        hr =  VFW_E_ENUM_OUT_OF_SYNC;

+    } else {

+        *ppEnum = new CEnumPins(m_pFilter, 

+                                this);

+        if (*ppEnum == NULL) {

+            hr = E_OUTOFMEMORY;

+        }

+    }

+    return hr;

+}

+

+

+/* Return the next pin after the current position */

+

+STDMETHODIMP

+CEnumPins::Next(ULONG cPins,        // place this many pins...

+        __out_ecount(cPins) IPin **ppPins,      // ...in this array

+        __out_opt ULONG *pcFetched)   // actual count passed returned here

+{

+    CheckPointer(ppPins,E_POINTER);

+    ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));

+

+    ASSERT(ppPins);

+

+    if (pcFetched!=NULL) {

+        ValidateWritePtr(pcFetched, sizeof(ULONG));

+        *pcFetched = 0;           // default unless we succeed

+    }

+    // now check that the parameter is valid

+    else if (cPins>1) {   // pcFetched == NULL

+        return E_INVALIDARG;

+    }

+    ULONG cFetched = 0;           // increment as we get each one.

+

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        // If we are out of sync, we should refresh the enumerator.

+        // This will reset the position and update the other members, but

+        // will not clear cache of pins we have already returned.

+        Refresh();

+    }

+

+    /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed

+       so we must QI for the IPin (which increments its reference count)

+       If while we are retrieving a pin from the filter an error occurs we

+       assume that our internal state is stale with respect to the filter

+       (for example someone has deleted a pin) so we

+       return VFW_E_ENUM_OUT_OF_SYNC                            */

+

+    while (cFetched < cPins && m_PinCount > m_Position) {

+

+        /* Get the next pin object from the filter */

+

+        CBasePin *pPin = m_pFilter->GetPin(m_Position++);

+        if (pPin == NULL) {

+            // If this happend, and it's not the first time through, then we've got a problem,

+            // since we should really go back and release the iPins, which we have previously

+            // AddRef'ed.

+            ASSERT( cFetched==0 );

+            return VFW_E_ENUM_OUT_OF_SYNC;

+        }

+

+        /* We only want to return this pin, if it is not in our cache */

+        if (0 == m_PinCache.Find(pPin))

+        {

+            /* From the object get an IPin interface */

+

+            *ppPins = pPin;

+            pPin->AddRef();

+

+            cFetched++;

+            ppPins++;

+

+            m_PinCache.AddTail(pPin);

+        }

+    }

+

+    if (pcFetched!=NULL) {

+        *pcFetched = cFetched;

+    }

+

+    return (cPins==cFetched ? NOERROR : S_FALSE);

+}

+

+

+/* Skip over one or more entries in the enumerator */

+

+STDMETHODIMP

+CEnumPins::Skip(ULONG cPins)

+{

+    /* Check we are still in sync with the filter */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    /* Work out how many pins are left to skip over */

+    /* We could position at the end if we are asked to skip too many... */

+    /* ..which would match the base implementation for CEnumMediaTypes::Skip */

+

+    ULONG PinsLeft = m_PinCount - m_Position;

+    if (cPins > PinsLeft) {

+        return S_FALSE;

+    }

+    m_Position += cPins;

+    return NOERROR;

+}

+

+

+/* Set the current position back to the start */

+/* Reset has 4 simple steps:

+ *

+ * Set position to head of list

+ * Sync enumerator with object being enumerated

+ * Clear the cache of pins already returned

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumPins::Reset()

+{

+    m_Version = m_pFilter->GetPinVersion();

+    m_PinCount = m_pFilter->GetPinCount();

+

+    m_Position = 0;

+

+    // Clear the cache

+    m_PinCache.RemoveAll();

+

+    return S_OK;

+}

+

+

+/* Set the current position back to the start */

+/* Refresh has 3 simple steps:

+ *

+ * Set position to head of list

+ * Sync enumerator with object being enumerated

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumPins::Refresh()

+{

+    m_Version = m_pFilter->GetPinVersion();

+    m_PinCount = m_pFilter->GetPinCount();

+

+    m_Position = 0;

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CEnumMediaTypes

+//=====================================================================

+//=====================================================================

+

+

+CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,

+                                 __in_opt CEnumMediaTypes *pEnumMediaTypes) :

+    m_Position(0),

+    m_pPin(pPin),

+    m_cRef(1)

+{

+

+#ifdef DEBUG

+    m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);

+#endif

+

+    /* We must be owned by a pin derived from CBasePin */

+

+    ASSERT(pPin != NULL);

+

+    /* Hold a reference count on our pin */

+    m_pPin->AddRef();

+

+    /* Are we creating a new enumerator */

+

+    if (pEnumMediaTypes == NULL) {

+        m_Version = m_pPin->GetMediaTypeVersion();

+        return;

+    }

+

+    m_Position = pEnumMediaTypes->m_Position;

+    m_Version = pEnumMediaTypes->m_Version;

+}

+

+

+/* Destructor releases the reference count on our base pin. NOTE since we hold

+   a reference count on the pin who created us we know it is safe to release

+   it, no access can be made to it afterwards though as we might have just

+   caused the last reference count to go and the object to be deleted */

+

+CEnumMediaTypes::~CEnumMediaTypes()

+{

+#ifdef DEBUG

+    DbgRegisterObjectDestruction(m_dwCookie);

+#endif

+    m_pPin->Release();

+}

+

+

+/* Override this to say what interfaces we support where */

+

+STDMETHODIMP

+CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    CheckPointer(ppv, E_POINTER);

+

+    /* Do we have this interface */

+

+    if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {

+        return GetInterface((IEnumMediaTypes *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CEnumMediaTypes::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+STDMETHODIMP_(ULONG)

+CEnumMediaTypes::Release()

+{

+    ULONG cRef = InterlockedDecrement(&m_cRef);

+    if (cRef == 0) {

+        delete this;

+    }

+    return cRef;

+}

+

+/* One of an enumerator's basic member functions allows us to create a cloned

+   interface that initially has the same state. Since we are taking a snapshot

+   of an object (current position and all) we must lock access at the start */

+

+STDMETHODIMP

+CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));

+    HRESULT hr = NOERROR;

+

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        *ppEnum = NULL;

+        hr = VFW_E_ENUM_OUT_OF_SYNC;

+    } else {

+

+        *ppEnum = new CEnumMediaTypes(m_pPin,

+                                      this);

+

+        if (*ppEnum == NULL) {

+            hr =  E_OUTOFMEMORY;

+        }

+    }

+    return hr;

+}

+

+

+/* Enumerate the next pin(s) after the current position. The client using this

+   interface passes in a pointer to an array of pointers each of which will

+   be filled in with a pointer to a fully initialised media type format

+   Return NOERROR if it all works,

+          S_FALSE if fewer than cMediaTypes were enumerated.

+          VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by

+                                 state changes in the filter

+   The actual count always correctly reflects the number of types in the array.

+*/

+

+STDMETHODIMP

+CEnumMediaTypes::Next(ULONG cMediaTypes,          // place this many types...

+                      __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes,   // ...in this array

+                      __out ULONG *pcFetched)           // actual count passed

+{

+    CheckPointer(ppMediaTypes,E_POINTER);

+    ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    if (pcFetched!=NULL) {

+        ValidateWritePtr(pcFetched, sizeof(ULONG));

+        *pcFetched = 0;           // default unless we succeed

+    }

+    // now check that the parameter is valid

+    else if (cMediaTypes>1) {     // pcFetched == NULL

+        return E_INVALIDARG;

+    }

+    ULONG cFetched = 0;           // increment as we get each one.

+

+    /* Return each media type by asking the filter for them in turn - If we

+       have an error code retured to us while we are retrieving a media type

+       we assume that our internal state is stale with respect to the filter

+       (for example the window size changing) so we return

+       VFW_E_ENUM_OUT_OF_SYNC */

+

+    while (cMediaTypes) {

+

+        CMediaType cmt;

+

+        HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);

+        if (S_OK != hr) {

+            break;

+        }

+

+        /* We now have a CMediaType object that contains the next media type

+           but when we assign it to the array position we CANNOT just assign

+           the AM_MEDIA_TYPE structure because as soon as the object goes out of

+           scope it will delete the memory we have just copied. The function

+           we use is CreateMediaType which allocates a task memory block */

+

+        /*  Transfer across the format block manually to save an allocate

+            and free on the format block and generally go faster */

+

+        *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));

+        if (*ppMediaTypes == NULL) {

+            break;

+        }

+

+        /*  Do a regular copy */

+        **ppMediaTypes = cmt;

+

+        /*  Make sure the destructor doesn't free these */

+        cmt.pbFormat = NULL;

+        cmt.cbFormat = NULL;

+        cmt.pUnk     = NULL;

+

+

+        ppMediaTypes++;

+        cFetched++;

+        cMediaTypes--;

+    }

+

+    if (pcFetched!=NULL) {

+        *pcFetched = cFetched;

+    }

+

+    return ( cMediaTypes==0 ? NOERROR : S_FALSE );

+}

+

+

+/* Skip over one or more entries in the enumerator */

+

+STDMETHODIMP

+CEnumMediaTypes::Skip(ULONG cMediaTypes)

+{

+    //  If we're skipping 0 elements we're guaranteed to skip the

+    //  correct number of elements

+    if (cMediaTypes == 0) {

+        return S_OK;

+    }

+

+    /* Check we are still in sync with the pin */

+    if (AreWeOutOfSync() == TRUE) {

+        return VFW_E_ENUM_OUT_OF_SYNC;

+    }

+

+    m_Position += cMediaTypes;

+

+    /*  See if we're over the end */

+    CMediaType cmt;

+    return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;

+}

+

+

+/* Set the current position back to the start */

+/* Reset has 3 simple steps:

+ *

+ * set position to head of list

+ * sync enumerator with object being enumerated

+ * return S_OK

+ */

+

+STDMETHODIMP

+CEnumMediaTypes::Reset()

+

+{

+    m_Position = 0;

+

+    // Bring the enumerator back into step with the current state.  This

+    // may be a noop but ensures that the enumerator will be valid on the

+    // next call.

+    m_Version = m_pPin->GetMediaTypeVersion();

+    return NOERROR;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBasePin

+//=====================================================================

+//=====================================================================

+

+

+/* NOTE The implementation of this class calls the CUnknown constructor with

+   a NULL outer unknown pointer. This has the effect of making us a self

+   contained class, ie any QueryInterface, AddRef or Release calls will be

+   routed to the class's NonDelegatingUnknown methods. You will typically

+   find that the classes that do this then override one or more of these

+   virtual functions to provide more specialised behaviour. A good example

+   of this is where a class wants to keep the QueryInterface internal but

+   still wants its lifetime controlled by the external object */

+

+/* Constructor */

+

+CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,

+           __in CBaseFilter *pFilter,

+           __in CCritSec *pLock,

+           __inout HRESULT *phr,

+           __in_opt LPCWSTR pName,

+           PIN_DIRECTION dir) :

+    CUnknown( pObjectName, NULL ),

+    m_pFilter(pFilter),

+    m_pLock(pLock),

+    m_pName(NULL),

+    m_Connected(NULL),

+    m_dir(dir),

+    m_bRunTimeError(FALSE),

+    m_pQSink(NULL),

+    m_TypeVersion(1),

+    m_tStart(),

+    m_tStop(MAX_TIME),

+    m_bCanReconnectWhenActive(false),

+    m_bTryMyTypesFirst(false),

+    m_dRate(1.0)

+{

+    /*  WARNING - pFilter is often not a properly constituted object at

+        this state (in particular QueryInterface may not work) - this

+        is because its owner is often its containing object and we

+        have been called from the containing object's constructor so

+        the filter's owner has not yet had its CUnknown constructor

+        called

+    */

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    ASSERT(pFilter != NULL);

+    ASSERT(pLock != NULL);

+

+    if (pName) {

+        size_t cchName;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);

+        if (SUCCEEDED(hr)) {

+            m_pName = new WCHAR[cchName + 1];

+            if (m_pName) {

+                (void)StringCchCopyW(m_pName, cchName + 1, pName);

+            }

+        }

+    }

+

+#ifdef DEBUG

+    m_cRef = 0;

+#endif

+}

+

+#ifdef UNICODE

+CBasePin::CBasePin(__in_opt LPCSTR pObjectName,

+           __in CBaseFilter *pFilter,

+           __in CCritSec *pLock,

+           __inout HRESULT *phr,

+           __in_opt LPCWSTR pName,

+           PIN_DIRECTION dir) :

+    CUnknown( pObjectName, NULL ),

+    m_pFilter(pFilter),

+    m_pLock(pLock),

+    m_pName(NULL),

+    m_Connected(NULL),

+    m_dir(dir),

+    m_bRunTimeError(FALSE),

+    m_pQSink(NULL),

+    m_TypeVersion(1),

+    m_tStart(),

+    m_tStop(MAX_TIME),

+    m_bCanReconnectWhenActive(false),

+    m_bTryMyTypesFirst(false),

+    m_dRate(1.0)

+{

+    /*  WARNING - pFilter is often not a properly constituted object at

+        this state (in particular QueryInterface may not work) - this

+        is because its owner is often its containing object and we

+        have been called from the containing object's constructor so

+        the filter's owner has not yet had its CUnknown constructor

+        called

+    */

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    ASSERT(pFilter != NULL);

+    ASSERT(pLock != NULL);

+

+    if (pName) {

+        size_t cchName;

+        HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);

+        if (SUCCEEDED(hr)) {

+            m_pName = new WCHAR[cchName + 1];

+            if (m_pName) {

+                (void)StringCchCopyW(m_pName, cchName + 1, pName);

+            }

+        }

+    }

+

+

+#ifdef DEBUG

+    m_cRef = 0;

+#endif

+}

+#endif

+

+/* Destructor since a connected pin holds a reference count on us there is

+   no way that we can be deleted unless we are not currently connected */

+

+CBasePin::~CBasePin()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );

+#endif // DXMPERF

+

+    //  We don't call disconnect because if the filter is going away

+    //  all the pins must have a reference count of zero so they must

+    //  have been disconnected anyway - (but check the assumption)

+    ASSERT(m_Connected == FALSE);

+

+    delete[] m_pName;

+

+    // check the internal reference count is consistent

+    ASSERT(m_cRef == 0);

+}

+

+

+/* Override this to say what interfaces we support and where */

+

+STDMETHODIMP

+CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)

+{

+    /* Do we have this interface */

+

+    if (riid == IID_IPin) {

+        return GetInterface((IPin *) this, ppv);

+    } else if (riid == IID_IQualityControl) {

+        return GetInterface((IQualityControl *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* Override to increment the owning filter's reference count */

+

+STDMETHODIMP_(ULONG)

+CBasePin::NonDelegatingAddRef()

+{

+    ASSERT(InterlockedIncrement(&m_cRef) > 0);

+    return m_pFilter->AddRef();

+}

+

+

+/* Override to decrement the owning filter's reference count */

+

+STDMETHODIMP_(ULONG)

+CBasePin::NonDelegatingRelease()

+{

+    ASSERT(InterlockedDecrement(&m_cRef) >= 0);

+    return m_pFilter->Release();

+}

+

+

+/* Displays pin connection information */

+

+#ifdef DEBUG

+void

+CBasePin::DisplayPinInfo(IPin *pReceivePin)

+{

+

+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {

+        PIN_INFO ConnectPinInfo;

+        PIN_INFO ReceivePinInfo;

+

+        if (FAILED(QueryPinInfo(&ConnectPinInfo))) {

+            StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");

+        } else {

+            QueryPinInfoReleaseFilter(ConnectPinInfo);

+        }

+

+        if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {

+            StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");

+        } else {

+            QueryPinInfoReleaseFilter(ReceivePinInfo);

+        }

+

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ConnectPinInfo.achName));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    <%ls>"), ReceivePinInfo.achName));

+    }

+}

+#endif

+

+

+/* Displays general information on the pin media type */

+

+#ifdef DEBUG

+void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)

+{

+    UNREFERENCED_PARAMETER(pPin);

+    if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    major type:  %hs"),

+               GuidNames[*pmt->Type()]));

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("    sub type  :  %hs"),

+               GuidNames[*pmt->Subtype()]));

+    }

+}

+#endif

+

+/* Asked to connect to a pin. A pin is always attached to an owning filter

+   object so we always delegate our locking to that object. We first of all

+   retrieve a media type enumerator for the input pin and see if we accept

+   any of the formats that it would ideally like, failing that we retrieve

+   our enumerator and see if it will accept any of our preferred types */

+

+STDMETHODIMP

+CBasePin::Connect(

+    IPin * pReceivePin,

+    __in_opt const AM_MEDIA_TYPE *pmt   // optional media type

+)

+{

+    CheckPointer(pReceivePin,E_POINTER);

+    ValidateReadPtr(pReceivePin,sizeof(IPin));

+    CAutoLock cObjectLock(m_pLock);

+    DisplayPinInfo(pReceivePin);

+

+    /* See if we are already connected */

+

+    if (m_Connected) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));

+        return VFW_E_ALREADY_CONNECTED;

+    }

+

+    /* See if the filter is active */

+    if (!IsStopped() && !m_bCanReconnectWhenActive) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+

+    // Find a mutually agreeable media type -

+    // Pass in the template media type. If this is partially specified,

+    // each of the enumerated media types will need to be checked against

+    // it. If it is non-null and fully specified, we will just try to connect

+    // with this.

+

+    const CMediaType * ptype = (CMediaType*)pmt;

+    HRESULT hr = AgreeMediaType(pReceivePin, ptype);

+    if (FAILED(hr)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+        PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));

+

+#ifdef DXMPERF

+    PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+// given a specific media type, attempt a connection (includes

+// checking that the type is acceptable to this pin)

+HRESULT

+CBasePin::AttemptConnection(

+    IPin* pReceivePin,      // connect to this pin

+    const CMediaType* pmt   // using this type

+)

+{

+    // The caller should hold the filter lock becasue this function

+    // uses m_Connected.  The caller should also hold the filter lock

+    // because this function calls SetMediaType(), IsStopped() and

+    // CompleteConnect().

+    ASSERT(CritCheckIn(m_pLock));

+

+    // Check that the connection is valid  -- need to do this for every

+    // connect attempt since BreakConnect will undo it.

+    HRESULT hr = CheckConnect(pReceivePin);

+    if (FAILED(hr)) {

+        DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+        return hr;

+    }

+

+    DisplayTypeInfo(pReceivePin, pmt);

+

+    /* Check we will accept this media type */

+

+    hr = CheckMediaType(pmt);

+    if (hr == NOERROR) {

+

+        /*  Make ourselves look connected otherwise ReceiveConnection

+            may not be able to complete the connection

+        */

+        m_Connected = pReceivePin;

+        m_Connected->AddRef();

+        hr = SetMediaType(pmt);

+        if (SUCCEEDED(hr)) {

+            /* See if the other pin will accept this type */

+

+            hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);

+            if (SUCCEEDED(hr)) {

+                /* Complete the connection */

+

+                hr = CompleteConnect(pReceivePin);

+                if (SUCCEEDED(hr)) {

+                    return hr;

+                } else {

+                    DbgLog((LOG_TRACE,

+                            CONNECT_TRACE_LEVEL,

+                            TEXT("Failed to complete connection")));

+                    pReceivePin->Disconnect();

+                }

+            }

+        }

+    } else {

+        // we cannot use this media type

+

+        // return a specific media type error if there is one

+        // or map a general failure code to something more helpful

+        // (in particular S_FALSE gets changed to an error code)

+        if (SUCCEEDED(hr) ||

+            (hr == E_FAIL) ||

+            (hr == E_INVALIDARG)) {

+            hr = VFW_E_TYPE_NOT_ACCEPTED;

+        }

+    }

+

+    // BreakConnect and release any connection here in case CheckMediaType

+    // failed, or if we set anything up during a call back during

+    // ReceiveConnection.

+

+    // Since the procedure is already returning an error code, there

+    // is nothing else this function can do to report the error.

+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+    /*  If failed then undo our state */

+    if (m_Connected) {

+        m_Connected->Release();

+        m_Connected = NULL;

+    }

+

+    return hr;

+}

+

+/* Given an enumerator we cycle through all the media types it proposes and

+   firstly suggest them to our derived pin class and if that succeeds try

+   them with the pin in a ReceiveConnection call. This means that if our pin

+   proposes a media type we still check in here that we can support it. This

+   is deliberate so that in simple cases the enumerator can hold all of the

+   media types even if some of them are not really currently available */

+

+HRESULT CBasePin::TryMediaTypes(

+    IPin *pReceivePin,

+    __in_opt const CMediaType *pmt,

+    IEnumMediaTypes *pEnum)

+{

+    /* Reset the current enumerator position */

+

+    HRESULT hr = pEnum->Reset();

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    CMediaType *pMediaType = NULL;

+    ULONG ulMediaCount = 0;

+

+    // attempt to remember a specific error code if there is one

+    HRESULT hrFailure = S_OK;

+

+    for (;;) {

+

+        /* Retrieve the next media type NOTE each time round the loop the

+           enumerator interface will allocate another AM_MEDIA_TYPE structure

+           If we are successful then we copy it into our output object, if

+           not then we must delete the memory allocated before returning */

+

+        hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);

+        if (hr != S_OK) {

+            if (S_OK == hrFailure) {

+                hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;

+            }

+            return hrFailure;

+        }

+

+

+        ASSERT(ulMediaCount == 1);

+        ASSERT(pMediaType);

+

+        // check that this matches the partial type (if any)

+

+        if (pMediaType &&

+            ((pmt == NULL) ||

+            pMediaType->MatchesPartial(pmt))) {

+

+            hr = AttemptConnection(pReceivePin, pMediaType);

+

+            // attempt to remember a specific error code

+            if (FAILED(hr) &&

+            SUCCEEDED(hrFailure) &&

+            (hr != E_FAIL) &&

+            (hr != E_INVALIDARG) &&

+            (hr != VFW_E_TYPE_NOT_ACCEPTED)) {

+                hrFailure = hr;

+            }

+        } else {

+            hr = VFW_E_NO_ACCEPTABLE_TYPES;

+        }

+

+        if(pMediaType) {

+            DeleteMediaType(pMediaType);

+            pMediaType = NULL;

+        }

+

+        if (S_OK == hr) {

+            return hr;

+        }

+    }

+}

+

+

+/* This is called to make the connection, including the taask of finding

+   a media type for the pin connection. pmt is the proposed media type

+   from the Connect call: if this is fully specified, we will try that.

+   Otherwise we enumerate and try all the input pin's types first and

+   if that fails we then enumerate and try all our preferred media types.

+   For each media type we check it against pmt (if non-null and partially

+   specified) as well as checking that both pins will accept it.

+ */

+

+HRESULT CBasePin::AgreeMediaType(

+    IPin *pReceivePin,

+    const CMediaType *pmt)

+{

+    ASSERT(pReceivePin);

+    IEnumMediaTypes *pEnumMediaTypes = NULL;

+

+    // if the media type is fully specified then use that

+    if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {

+

+        // if this media type fails, then we must fail the connection

+        // since if pmt is nonnull we are only allowed to connect

+        // using a type that matches it.

+

+        return AttemptConnection(pReceivePin, pmt);

+    }

+

+

+    /* Try the other pin's enumerator */

+

+    HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;

+

+    for (int i = 0; i < 2; i++) {

+        HRESULT hr;

+        if (i == (int)m_bTryMyTypesFirst) {

+            hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);

+        } else {

+            hr = EnumMediaTypes(&pEnumMediaTypes);

+        }

+        if (SUCCEEDED(hr)) {

+            ASSERT(pEnumMediaTypes);

+            hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);

+            pEnumMediaTypes->Release();

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            } else {

+                // try to remember specific error codes if there are any

+                if ((hr != E_FAIL) &&

+                    (hr != E_INVALIDARG) &&

+                    (hr != VFW_E_TYPE_NOT_ACCEPTED)) {

+                    hrFailure = hr;

+                }

+            }

+        }

+    }

+

+    return hrFailure;

+}

+

+

+/* Called when we want to complete a connection to another filter. Failing

+   this will also fail the connection and disconnect the other pin as well */

+

+HRESULT

+CBasePin::CompleteConnect(IPin *pReceivePin)

+{

+    UNREFERENCED_PARAMETER(pReceivePin);

+    return NOERROR;

+}

+

+

+/* This is called to set the format for a pin connection - CheckMediaType

+   will have been called to check the connection format and if it didn't

+   return an error code then this (virtual) function will be invoked */

+

+HRESULT

+CBasePin::SetMediaType(const CMediaType *pmt)

+{

+    HRESULT hr = m_mt.Set(*pmt);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    return NOERROR;

+}

+

+

+/* This is called during Connect() to provide a virtual method that can do

+   any specific check needed for connection such as QueryInterface. This

+   base class method just checks that the pin directions don't match */

+

+HRESULT

+CBasePin::CheckConnect(IPin * pPin)

+{

+    /* Check that pin directions DONT match */

+

+    PIN_DIRECTION pd;

+    pPin->QueryDirection(&pd);

+

+    ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));

+    ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));

+

+    // we should allow for non-input and non-output connections?

+    if (pd == m_dir) {

+        return VFW_E_INVALID_DIRECTION;

+    }

+    return NOERROR;

+}

+

+

+/* This is called when we realise we can't make a connection to the pin and

+   must undo anything we did in CheckConnect - override to release QIs done */

+

+HRESULT

+CBasePin::BreakConnect()

+{

+    return NOERROR;

+}

+

+

+/* Called normally by an output pin on an input pin to try and establish a

+   connection.

+*/

+

+STDMETHODIMP

+CBasePin::ReceiveConnection(

+    IPin * pConnector,   // this is the pin who we will connect to

+    const AM_MEDIA_TYPE *pmt  // this is the media type we will exchange

+)

+{

+    CheckPointer(pConnector,E_POINTER);

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadPtr(pConnector,sizeof(IPin));

+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));

+    CAutoLock cObjectLock(m_pLock);

+

+    /* Are we already connected */

+    if (m_Connected) {

+        return VFW_E_ALREADY_CONNECTED;

+    }

+

+    /* See if the filter is active */

+    if (!IsStopped() && !m_bCanReconnectWhenActive) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+    HRESULT hr = CheckConnect(pConnector);

+    if (FAILED(hr)) {

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    /* Ask derived class if this media type is ok */

+

+    CMediaType * pcmt = (CMediaType*) pmt;

+    hr = CheckMediaType(pcmt);

+    if (hr != NOERROR) {

+        // no -we don't support this media type

+

+        // Since the procedure is already returning an error code, there

+        // is nothing else this function can do to report the error.

+        EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+        // return a specific media type error if there is one

+        // or map a general failure code to something more helpful

+        // (in particular S_FALSE gets changed to an error code)

+        if (SUCCEEDED(hr) ||

+            (hr == E_FAIL) ||

+            (hr == E_INVALIDARG)) {

+            hr = VFW_E_TYPE_NOT_ACCEPTED;

+        }

+

+#ifdef DXMPERF

+        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+        return hr;

+    }

+

+    /* Complete the connection */

+

+    m_Connected = pConnector;

+    m_Connected->AddRef();

+    hr = SetMediaType(pcmt);

+    if (SUCCEEDED(hr)) {

+        hr = CompleteConnect(pConnector);

+        if (SUCCEEDED(hr)) {

+

+#ifdef DXMPERF

+            PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );

+#endif // DXMPERF

+

+            return NOERROR;

+        }

+    }

+

+    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));

+    m_Connected->Release();

+    m_Connected = NULL;

+

+    // Since the procedure is already returning an error code, there

+    // is nothing else this function can do to report the error.

+    EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );

+

+#ifdef DXMPERF

+    PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );

+#endif // DXMPERF

+

+    return hr;

+}

+

+

+/* Called when we want to terminate a pin connection */

+

+STDMETHODIMP

+CBasePin::Disconnect()

+{

+    CAutoLock cObjectLock(m_pLock);

+

+    /* See if the filter is active */

+    if (!IsStopped()) {

+        return VFW_E_NOT_STOPPED;

+    }

+

+    return DisconnectInternal();

+}

+

+STDMETHODIMP

+CBasePin::DisconnectInternal()

+{

+    ASSERT(CritCheckIn(m_pLock));

+

+    if (m_Connected) {

+        HRESULT hr = BreakConnect();

+        if( FAILED( hr ) ) {

+

+#ifdef DXMPERF

+            PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );

+#endif // DXMPERF

+

+            // There is usually a bug in the program if BreakConnect() fails.

+            DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );

+            return hr;

+        }

+

+        m_Connected->Release();

+        m_Connected = NULL;

+

+#ifdef DXMPERF

+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );

+#endif // DXMPERF

+

+        return S_OK;

+    } else {

+        // no connection - not an error

+

+#ifdef DXMPERF

+        PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );

+#endif // DXMPERF

+

+        return S_FALSE;

+    }

+}

+

+

+/* Return an AddRef()'d pointer to the connected pin if there is one */

+STDMETHODIMP

+CBasePin::ConnectedTo(

+    __deref_out IPin **ppPin

+)

+{

+    CheckPointer(ppPin,E_POINTER);

+    ValidateReadWritePtr(ppPin,sizeof(IPin *));

+    //

+    //  It's pointless to lock here.

+    //  The caller should ensure integrity.

+    //

+

+    IPin *pPin = m_Connected;

+    *ppPin = pPin;

+    if (pPin != NULL) {

+        pPin->AddRef();

+        return S_OK;

+    } else {

+        ASSERT(*ppPin == NULL);

+        return VFW_E_NOT_CONNECTED;

+    }

+}

+

+/* Return the media type of the connection */

+STDMETHODIMP

+CBasePin::ConnectionMediaType(

+    __out AM_MEDIA_TYPE *pmt

+)

+{

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));

+    CAutoLock cObjectLock(m_pLock);

+

+    /*  Copy constructor of m_mt allocates the memory */

+    if (IsConnected()) {

+        CopyMediaType( pmt, &m_mt );

+        return S_OK;

+    } else {

+        ((CMediaType *)pmt)->InitMediaType();

+        return VFW_E_NOT_CONNECTED;

+    }

+}

+

+/* Return information about the filter we are connect to */

+

+STDMETHODIMP

+CBasePin::QueryPinInfo(

+    __out PIN_INFO * pInfo

+)

+{

+    CheckPointer(pInfo,E_POINTER);

+    ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));

+

+    pInfo->pFilter = m_pFilter;

+    if (m_pFilter) {

+        m_pFilter->AddRef();

+    }

+

+    if (m_pName) {

+        (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);

+    } else {

+        pInfo->achName[0] = L'\0';

+    }

+

+    pInfo->dir = m_dir;

+

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBasePin::QueryDirection(

+    __out PIN_DIRECTION * pPinDir

+)

+{

+    CheckPointer(pPinDir,E_POINTER);

+    ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));

+

+    *pPinDir = m_dir;

+    return NOERROR;

+}

+

+// Default QueryId to return the pin's name

+STDMETHODIMP

+CBasePin::QueryId(

+    __deref_out LPWSTR * Id

+)

+{

+    //  We're not going away because someone's got a pointer to us

+    //  so there's no need to lock

+

+    return AMGetWideString(Name(), Id);

+}

+

+/* Does this pin support this media type WARNING this interface function does

+   not lock the main object as it is meant to be asynchronous by nature - if

+   the media types you support depend on some internal state that is updated

+   dynamically then you will need to implement locking in a derived class */

+

+STDMETHODIMP

+CBasePin::QueryAccept(

+    const AM_MEDIA_TYPE *pmt

+)

+{

+    CheckPointer(pmt,E_POINTER);

+    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));

+

+    /* The CheckMediaType method is valid to return error codes if the media

+       type is horrible, an example might be E_INVALIDARG. What we do here

+       is map all the error codes into either S_OK or S_FALSE regardless */

+

+    HRESULT hr = CheckMediaType((CMediaType*)pmt);

+    if (FAILED(hr)) {

+        return S_FALSE;

+    }

+    // note that the only defined success codes should be S_OK and S_FALSE...

+    return hr;

+}

+

+

+/* This can be called to return an enumerator for the pin's list of preferred

+   media types. An input pin is not obliged to have any preferred formats

+   although it can do. For example, the window renderer has a preferred type

+   which describes a video image that matches the current window size. All

+   output pins should expose at least one preferred format otherwise it is

+   possible that neither pin has any types and so no connection is possible */

+

+STDMETHODIMP

+CBasePin::EnumMediaTypes(

+    __deref_out IEnumMediaTypes **ppEnum

+)

+{

+    CheckPointer(ppEnum,E_POINTER);

+    ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));

+

+    /* Create a new ref counted enumerator */

+

+    *ppEnum = new CEnumMediaTypes(this,

+                              NULL);

+

+    if (*ppEnum == NULL) {

+        return E_OUTOFMEMORY;

+    }

+

+    return NOERROR;

+}

+

+

+

+/* This is a virtual function that returns a media type corresponding with

+   place iPosition in the list. This base class simply returns an error as

+   we support no media types by default but derived classes should override */

+

+HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)

+{

+    UNREFERENCED_PARAMETER(iPosition);

+    UNREFERENCED_PARAMETER(pMediaType);

+    return E_UNEXPECTED;

+}

+

+

+/* This is a virtual function that returns the current media type version.

+   The base class initialises the media type enumerators with the value 1

+   By default we always returns that same value. A Derived class may change

+   the list of media types available and after doing so it should increment

+   the version either in a method derived from this, or more simply by just

+   incrementing the m_TypeVersion base pin variable. The type enumerators

+   call this when they want to see if their enumerations are out of date */

+

+LONG CBasePin::GetMediaTypeVersion()

+{

+    return m_TypeVersion;

+}

+

+

+/* Increment the cookie representing the current media type version */

+

+void CBasePin::IncrementTypeVersion()

+{

+    InterlockedIncrement(&m_TypeVersion);

+}

+

+

+/* Called by IMediaFilter implementation when the state changes from Stopped

+   to either paused or running and in derived classes could do things like

+   commit memory and grab hardware resource (the default is to do nothing) */

+

+HRESULT

+CBasePin::Active(void)

+{

+    return NOERROR;

+}

+

+/* Called by IMediaFilter implementation when the state changes from

+   to either paused to running and in derived classes could do things like

+   commit memory and grab hardware resource (the default is to do nothing) */

+

+HRESULT

+CBasePin::Run(REFERENCE_TIME tStart)

+{

+    UNREFERENCED_PARAMETER(tStart);

+    return NOERROR;

+}

+

+

+/* Also called by the IMediaFilter implementation when the state changes to

+   Stopped at which point you should decommit allocators and free hardware

+   resources you grabbed in the Active call (default is also to do nothing) */

+

+HRESULT

+CBasePin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    return NOERROR;

+}

+

+

+// Called when no more data will arrive

+STDMETHODIMP

+CBasePin::EndOfStream(void)

+{

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CBasePin::SetSink(IQualityControl * piqc)

+{

+    CAutoLock cObjectLock(m_pLock);

+    if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));

+    m_pQSink = piqc;

+    return NOERROR;

+} // SetSink

+

+

+STDMETHODIMP

+CBasePin::Notify(IBaseFilter * pSender, Quality q)

+{

+    UNREFERENCED_PARAMETER(q);

+    UNREFERENCED_PARAMETER(pSender);

+    DbgBreak("IQualityControl::Notify not over-ridden from CBasePin.  (IGNORE is OK)");

+    return E_NOTIMPL;

+} //Notify

+

+

+// NewSegment notifies of the start/stop/rate applying to the data

+// about to be received. Default implementation records data and

+// returns S_OK.

+// Override this to pass downstream.

+STDMETHODIMP

+CBasePin::NewSegment(

+                REFERENCE_TIME tStart,

+                REFERENCE_TIME tStop,

+                double dRate)

+{

+    m_tStart = tStart;

+    m_tStop = tStop;

+    m_dRate = dRate;

+

+    return S_OK;

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseOutputPin

+//=====================================================================

+//=====================================================================

+

+

+CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,

+                   __in CBaseFilter *pFilter,

+                   __in CCritSec *pLock,

+                   __inout HRESULT *phr,

+                   __in_opt LPCWSTR pName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),

+    m_pAllocator(NULL),

+    m_pInputPin(NULL)

+{

+    ASSERT(pFilter);

+}

+

+#ifdef UNICODE

+CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,

+                   __in CBaseFilter *pFilter,

+                   __in CCritSec *pLock,

+                   __inout HRESULT *phr,

+                   __in_opt LPCWSTR pName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),

+    m_pAllocator(NULL),

+    m_pInputPin(NULL)

+{

+    ASSERT(pFilter);

+}

+#endif

+

+/*   This is called after a media type has been proposed

+

+     Try to complete the connection by agreeing the allocator

+*/

+HRESULT

+CBaseOutputPin::CompleteConnect(IPin *pReceivePin)

+{

+    UNREFERENCED_PARAMETER(pReceivePin);

+    return DecideAllocator(m_pInputPin, &m_pAllocator);

+}

+

+

+/* This method is called when the output pin is about to try and connect to

+   an input pin. It is at this point that you should try and grab any extra

+   interfaces that you need, in this case IMemInputPin. Because this is

+   only called if we are not currently connected we do NOT need to call

+   BreakConnect. This also makes it easier to derive classes from us as

+   BreakConnect is only called when we actually have to break a connection

+   (or a partly made connection) and not when we are checking a connection */

+

+/* Overriden from CBasePin */

+

+HRESULT

+CBaseOutputPin::CheckConnect(IPin * pPin)

+{

+    HRESULT hr = CBasePin::CheckConnect(pPin);

+    if (FAILED(hr)) {

+    return hr;

+    }

+

+    // get an input pin and an allocator interface

+    hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);

+    if (FAILED(hr)) {

+        return hr;

+    }

+    return NOERROR;

+}

+

+

+/* Overriden from CBasePin */

+

+HRESULT

+CBaseOutputPin::BreakConnect()

+{

+    /* Release any allocator we hold */

+

+    if (m_pAllocator) {

+        // Always decommit the allocator because a downstream filter may or

+        // may not decommit the connection's allocator.  A memory leak could

+        // occur if the allocator is not decommited when a connection is broken.

+        HRESULT hr = m_pAllocator->Decommit();

+        if( FAILED( hr ) ) {

+            return hr;

+        }

+

+        m_pAllocator->Release();

+        m_pAllocator = NULL;

+    }

+

+    /* Release any input pin interface we hold */

+

+    if (m_pInputPin) {

+        m_pInputPin->Release();

+        m_pInputPin = NULL;

+    }

+    return NOERROR;

+}

+

+

+/* This is called when the input pin didn't give us a valid allocator */

+

+HRESULT

+CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)

+{

+    return CreateMemoryAllocator(ppAlloc);

+}

+

+

+/* Decide on an allocator, override this if you want to use your own allocator

+   Override DecideBufferSize to call SetProperties. If the input pin fails

+   the GetAllocator call then this will construct a CMemAllocator and call

+   DecideBufferSize on that, and if that fails then we are completely hosed.

+   If the you succeed the DecideBufferSize call, we will notify the input

+   pin of the selected allocator. NOTE this is called during Connect() which

+   therefore looks after grabbing and locking the object's critical section */

+

+// We query the input pin for its requested properties and pass this to

+// DecideBufferSize to allow it to fulfill requests that it is happy

+// with (eg most people don't care about alignment and are thus happy to

+// use the downstream pin's alignment request).

+

+HRESULT

+CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)

+{

+    HRESULT hr = NOERROR;

+    *ppAlloc = NULL;

+

+    // get downstream prop request

+    // the derived class may modify this in DecideBufferSize, but

+    // we assume that he will consistently modify it the same way,

+    // so we only get it once

+    ALLOCATOR_PROPERTIES prop;

+    ZeroMemory(&prop, sizeof(prop));

+

+    // whatever he returns, we assume prop is either all zeros

+    // or he has filled it out.

+    pPin->GetAllocatorRequirements(&prop);

+

+    // if he doesn't care about alignment, then set it to 1

+    if (prop.cbAlign == 0) {

+        prop.cbAlign = 1;

+    }

+

+    /* Try the allocator provided by the input pin */

+

+    hr = pPin->GetAllocator(ppAlloc);

+    if (SUCCEEDED(hr)) {

+

+        hr = DecideBufferSize(*ppAlloc, &prop);

+        if (SUCCEEDED(hr)) {

+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            }

+        }

+    }

+

+    /* If the GetAllocator failed we may not have an interface */

+

+    if (*ppAlloc) {

+        (*ppAlloc)->Release();

+        *ppAlloc = NULL;

+    }

+

+    /* Try the output pin's allocator by the same method */

+

+    hr = InitAllocator(ppAlloc);

+    if (SUCCEEDED(hr)) {

+

+        // note - the properties passed here are in the same

+        // structure as above and may have been modified by

+        // the previous call to DecideBufferSize

+        hr = DecideBufferSize(*ppAlloc, &prop);

+        if (SUCCEEDED(hr)) {

+            hr = pPin->NotifyAllocator(*ppAlloc, FALSE);

+            if (SUCCEEDED(hr)) {

+                return NOERROR;

+            }

+        }

+    }

+

+    /* Likewise we may not have an interface to release */

+

+    if (*ppAlloc) {

+        (*ppAlloc)->Release();

+        *ppAlloc = NULL;

+    }

+    return hr;

+}

+

+

+/* This returns an empty sample buffer from the allocator WARNING the same

+   dangers and restrictions apply here as described below for Deliver() */

+

+HRESULT

+CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,

+                                  __in_opt REFERENCE_TIME * pStartTime,

+                                  __in_opt REFERENCE_TIME * pEndTime,

+                                  DWORD dwFlags)

+{

+    if (m_pAllocator != NULL) {

+        return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);

+    } else {

+        return E_NOINTERFACE;

+    }

+}

+

+

+/* Deliver a filled-in sample to the connected input pin. NOTE the object must

+   have locked itself before calling us otherwise we may get halfway through

+   executing this method only to find the filter graph has got in and

+   disconnected us from the input pin. If the filter has no worker threads

+   then the lock is best applied on Receive(), otherwise it should be done

+   when the worker thread is ready to deliver. There is a wee snag to worker

+   threads that this shows up. The worker thread must lock the object when

+   it is ready to deliver a sample, but it may have to wait until a state

+   change has completed, but that may never complete because the state change

+   is waiting for the worker thread to complete. The way to handle this is for

+   the state change code to grab the critical section, then set an abort event

+   for the worker thread, then release the critical section and wait for the

+   worker thread to see the event we set and then signal that it has finished

+   (with another event). At which point the state change code can complete */

+

+// note (if you've still got any breath left after reading that) that you

+// need to release the sample yourself after this call. if the connected

+// input pin needs to hold onto the sample beyond the call, it will addref

+// the sample itself.

+

+// of course you must release this one and call GetDeliveryBuffer for the

+// next. You cannot reuse it directly.

+

+HRESULT

+CBaseOutputPin::Deliver(IMediaSample * pSample)

+{

+    if (m_pInputPin == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+

+#ifdef DXMPERF

+    PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin  *) m_pInputPin, pSample, &m_mt );

+#endif // DXMPERF

+

+    return m_pInputPin->Receive(pSample);

+}

+

+

+// called from elsewhere in our filter to pass EOS downstream to

+// our connected input pin

+HRESULT

+CBaseOutputPin::DeliverEndOfStream(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->EndOfStream();

+}

+

+

+/* Commit the allocator's memory, this is called through IMediaFilter

+   which is responsible for locking the object before calling us */

+

+HRESULT

+CBaseOutputPin::Active(void)

+{

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+    return m_pAllocator->Commit();

+}

+

+

+/* Free up or unprepare allocator's memory, this is called through

+   IMediaFilter which is responsible for locking the object first */

+

+HRESULT

+CBaseOutputPin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+    return m_pAllocator->Decommit();

+}

+

+// we have a default handling of EndOfStream which is to return

+// an error, since this should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::EndOfStream(void)

+{

+    return E_UNEXPECTED;

+}

+

+

+// BeginFlush should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::BeginFlush(void)

+{

+    return E_UNEXPECTED;

+}

+

+// EndFlush should be called on input pins only

+STDMETHODIMP

+CBaseOutputPin::EndFlush(void)

+{

+    return E_UNEXPECTED;

+}

+

+// call BeginFlush on the connected input pin

+HRESULT

+CBaseOutputPin::DeliverBeginFlush(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->BeginFlush();

+}

+

+// call EndFlush on the connected input pin

+HRESULT

+CBaseOutputPin::DeliverEndFlush(void)

+{

+    // remember this is on IPin not IMemInputPin

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->EndFlush();

+}

+// deliver NewSegment to connected pin

+HRESULT

+CBaseOutputPin::DeliverNewSegment(

+    REFERENCE_TIME tStart,

+    REFERENCE_TIME tStop,

+    double dRate)

+{

+    if (m_Connected == NULL) {

+        return VFW_E_NOT_CONNECTED;

+    }

+    return m_Connected->NewSegment(tStart, tStop, dRate);

+}

+

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseInputPin

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor creates a default allocator object */

+

+CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,

+                 __in CBaseFilter *pFilter,

+                 __in CCritSec *pLock,

+                 __inout HRESULT *phr,

+                 __in_opt LPCWSTR pPinName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),

+    m_pAllocator(NULL),

+    m_bReadOnly(FALSE),

+    m_bFlushing(FALSE)

+{

+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));

+}

+

+#ifdef UNICODE

+CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,

+                 __in CBaseFilter *pFilter,

+                 __in CCritSec *pLock,

+                 __inout HRESULT *phr,

+                 __in_opt LPCWSTR pPinName) :

+    CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),

+    m_pAllocator(NULL),

+    m_bReadOnly(FALSE),

+    m_bFlushing(FALSE)

+{

+    ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));

+}

+#endif

+

+/* Destructor releases it's reference count on the default allocator */

+

+CBaseInputPin::~CBaseInputPin()

+{

+    if (m_pAllocator != NULL) {

+    m_pAllocator->Release();

+    m_pAllocator = NULL;

+    }

+}

+

+

+// override this to publicise our interfaces

+STDMETHODIMP

+CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    /* Do we know about this interface */

+

+    if (riid == IID_IMemInputPin) {

+        return GetInterface((IMemInputPin *) this, ppv);

+    } else {

+        return CBasePin::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* Return the allocator interface that this input pin would like the output

+   pin to use. NOTE subsequent calls to GetAllocator should all return an

+   interface onto the SAME object so we create one object at the start

+

+   Note:

+       The allocator is Release()'d on disconnect and replaced on

+       NotifyAllocator().

+

+   Override this to provide your own allocator.

+*/

+

+STDMETHODIMP

+CBaseInputPin::GetAllocator(

+    __deref_out IMemAllocator **ppAllocator)

+{

+    CheckPointer(ppAllocator,E_POINTER);

+    ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));

+    CAutoLock cObjectLock(m_pLock);

+

+    if (m_pAllocator == NULL) {

+        HRESULT hr = CreateMemoryAllocator(&m_pAllocator);

+        if (FAILED(hr)) {

+            return hr;

+        }

+    }

+    ASSERT(m_pAllocator != NULL);

+    *ppAllocator = m_pAllocator;

+    m_pAllocator->AddRef();

+    return NOERROR;

+}

+

+

+/* Tell the input pin which allocator the output pin is actually going to use

+   Override this if you care - NOTE the locking we do both here and also in

+   GetAllocator is unnecessary but derived classes that do something useful

+   will undoubtedly have to lock the object so this might help remind people */

+

+STDMETHODIMP

+CBaseInputPin::NotifyAllocator(

+    IMemAllocator * pAllocator,

+    BOOL bReadOnly)

+{

+    CheckPointer(pAllocator,E_POINTER);

+    ValidateReadPtr(pAllocator,sizeof(IMemAllocator));

+    CAutoLock cObjectLock(m_pLock);

+

+    IMemAllocator *pOldAllocator = m_pAllocator;

+    pAllocator->AddRef();

+    m_pAllocator = pAllocator;

+

+    if (pOldAllocator != NULL) {

+        pOldAllocator->Release();

+    }

+

+    // the readonly flag indicates whether samples from this allocator should

+    // be regarded as readonly - if true, then inplace transforms will not be

+    // allowed.

+    m_bReadOnly = (BYTE)bReadOnly;

+    return NOERROR;

+}

+

+

+HRESULT

+CBaseInputPin::BreakConnect()

+{

+    /* We don't need our allocator any more */

+    if (m_pAllocator) {

+        // Always decommit the allocator because a downstream filter may or

+        // may not decommit the connection's allocator.  A memory leak could

+        // occur if the allocator is not decommited when a pin is disconnected.

+        HRESULT hr = m_pAllocator->Decommit();

+        if( FAILED( hr ) ) {

+            return hr;

+        }

+

+        m_pAllocator->Release();

+        m_pAllocator = NULL;

+    }

+

+    return S_OK;

+}

+

+

+/* Do something with this media sample - this base class checks to see if the

+   format has changed with this media sample and if so checks that the filter

+   will accept it, generating a run time error if not. Once we have raised a

+   run time error we set a flag so that no more samples will be accepted

+

+   It is important that any filter should override this method and implement

+   synchronization so that samples are not processed when the pin is

+   disconnected etc

+*/

+

+STDMETHODIMP

+CBaseInputPin::Receive(IMediaSample *pSample)

+{

+    CheckPointer(pSample,E_POINTER);

+    ValidateReadPtr(pSample,sizeof(IMediaSample));

+    ASSERT(pSample);

+

+    HRESULT hr = CheckStreaming();

+    if (S_OK != hr) {

+        return hr;

+    }

+

+#ifdef DXMPERF

+    PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );

+#endif // DXMPERF

+

+

+    /* Check for IMediaSample2 */

+    IMediaSample2 *pSample2;

+    if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {

+        hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);

+        pSample2->Release();

+        if (FAILED(hr)) {

+            return hr;

+        }

+    } else {

+        /*  Get the properties the hard way */

+        m_SampleProps.cbData = sizeof(m_SampleProps);

+        m_SampleProps.dwTypeSpecificFlags = 0;

+        m_SampleProps.dwStreamId = AM_STREAM_MEDIA;

+        m_SampleProps.dwSampleFlags = 0;

+        if (S_OK == pSample->IsDiscontinuity()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;

+        }

+        if (S_OK == pSample->IsPreroll()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;

+        }

+        if (S_OK == pSample->IsSyncPoint()) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;

+        }

+        if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,

+                                       &m_SampleProps.tStop))) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |

+                                           AM_SAMPLE_STOPVALID;

+        }

+        if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {

+            m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;

+        }

+        pSample->GetPointer(&m_SampleProps.pbBuffer);

+        m_SampleProps.lActual = pSample->GetActualDataLength();

+        m_SampleProps.cbBuffer = pSample->GetSize();

+    }

+

+    /* Has the format changed in this sample */

+

+    if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {

+        return NOERROR;

+    }

+

+    /* Check the derived class accepts this format */

+    /* This shouldn't fail as the source must call QueryAccept first */

+

+    hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);

+

+    if (hr == NOERROR) {

+        return NOERROR;

+    }

+

+    /* Raise a runtime error if we fail the media type */

+

+    m_bRunTimeError = TRUE;

+    EndOfStream();

+    m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);

+    return VFW_E_INVALIDMEDIATYPE;

+}

+

+

+/*  Receive multiple samples */

+STDMETHODIMP

+CBaseInputPin::ReceiveMultiple (

+    __in_ecount(nSamples) IMediaSample **pSamples,

+    long nSamples,

+    __out long *nSamplesProcessed)

+{

+    CheckPointer(pSamples,E_POINTER);

+    ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));

+

+    HRESULT hr = S_OK;

+    *nSamplesProcessed = 0;

+    while (nSamples-- > 0) {

+         hr = Receive(pSamples[*nSamplesProcessed]);

+

+         /*  S_FALSE means don't send any more */

+         if (hr != S_OK) {

+             break;

+         }

+         (*nSamplesProcessed)++;

+    }

+    return hr;

+}

+

+/*  See if Receive() might block */

+STDMETHODIMP

+CBaseInputPin::ReceiveCanBlock()

+{

+    /*  Ask all the output pins if they block

+        If there are no output pin assume we do block

+    */

+    int cPins = m_pFilter->GetPinCount();

+    int cOutputPins = 0;

+    for (int c = 0; c < cPins; c++) {

+        CBasePin *pPin = m_pFilter->GetPin(c);

+        if (NULL == pPin) {

+            break;

+        }

+        PIN_DIRECTION pd;

+        HRESULT hr = pPin->QueryDirection(&pd);

+        if (FAILED(hr)) {

+            return hr;

+        }

+

+        if (pd == PINDIR_OUTPUT) {

+

+            IPin *pConnected;

+            hr = pPin->ConnectedTo(&pConnected);

+            if (SUCCEEDED(hr)) {

+                ASSERT(pConnected != NULL);

+                cOutputPins++;

+                IMemInputPin *pInputPin;

+                hr = pConnected->QueryInterface(

+                                              IID_IMemInputPin,

+                                              (void **)&pInputPin);

+                pConnected->Release();

+                if (SUCCEEDED(hr)) {

+                    hr = pInputPin->ReceiveCanBlock();

+                    pInputPin->Release();

+                    if (hr != S_FALSE) {

+                        return S_OK;

+                    }

+                } else {

+                    /*  There's a transport we don't understand here */

+                    return S_OK;

+                }

+            }

+        }

+    }

+    return cOutputPins == 0 ? S_OK : S_FALSE;

+}

+

+// Default handling for BeginFlush - call at the beginning

+// of your implementation (makes sure that all Receive calls

+// fail). After calling this, you need to free any queued data

+// and then call downstream.

+STDMETHODIMP

+CBaseInputPin::BeginFlush(void)

+{

+    //  BeginFlush is NOT synchronized with streaming but is part of

+    //  a control action - hence we synchronize with the filter

+    CAutoLock lck(m_pLock);

+

+    // if we are already in mid-flush, this is probably a mistake

+    // though not harmful - try to pick it up for now so I can think about it

+    ASSERT(!m_bFlushing);

+

+    // first thing to do is ensure that no further Receive calls succeed

+    m_bFlushing = TRUE;

+

+    // now discard any data and call downstream - must do that

+    // in derived classes

+    return S_OK;

+}

+

+// default handling for EndFlush - call at end of your implementation

+// - before calling this, ensure that there is no queued data and no thread

+// pushing any more without a further receive, then call downstream,

+// then call this method to clear the m_bFlushing flag and re-enable

+// receives

+STDMETHODIMP

+CBaseInputPin::EndFlush(void)

+{

+    //  Endlush is NOT synchronized with streaming but is part of

+    //  a control action - hence we synchronize with the filter

+    CAutoLock lck(m_pLock);

+

+    // almost certainly a mistake if we are not in mid-flush

+    ASSERT(m_bFlushing);

+

+    // before calling, sync with pushing thread and ensure

+    // no more data is going downstream, then call EndFlush on

+    // downstream pins.

+

+    // now re-enable Receives

+    m_bFlushing = FALSE;

+

+    // No more errors

+    m_bRunTimeError = FALSE;

+

+    return S_OK;

+}

+

+

+STDMETHODIMP

+CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)

+{

+    UNREFERENCED_PARAMETER(q);

+    CheckPointer(pSender,E_POINTER);

+    ValidateReadPtr(pSender,sizeof(IBaseFilter));

+    DbgBreak("IQuality::Notify called on an input pin");

+    return NOERROR;

+} // Notify

+

+/* Free up or unprepare allocator's memory, this is called through

+   IMediaFilter which is responsible for locking the object first */

+

+HRESULT

+CBaseInputPin::Inactive(void)

+{

+    m_bRunTimeError = FALSE;

+    if (m_pAllocator == NULL) {

+        return VFW_E_NO_ALLOCATOR;

+    }

+

+    m_bFlushing = FALSE;

+

+    return m_pAllocator->Decommit();

+}

+

+// what requirements do we have of the allocator - override if you want

+// to support other people's allocators but need a specific alignment

+// or prefix.

+STDMETHODIMP

+CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)

+{

+    UNREFERENCED_PARAMETER(pProps);

+    return E_NOTIMPL;

+}

+

+//  Check if it's OK to process data

+//

+HRESULT

+CBaseInputPin::CheckStreaming()

+{

+    //  Shouldn't be able to get any data if we're not connected!

+    ASSERT(IsConnected());

+

+    //  Don't process stuff in Stopped state

+    if (IsStopped()) {

+        return VFW_E_WRONG_STATE;

+    }

+    if (m_bFlushing) {

+        return S_FALSE;

+    }

+    if (m_bRunTimeError) {

+        return VFW_E_RUNTIME_ERROR;

+    }

+    return S_OK;

+}

+

+// Pass on the Quality notification q to

+// a. Our QualityControl sink (if we have one) or else

+// b. to our upstream filter

+// and if that doesn't work, throw it away with a bad return code

+HRESULT

+CBaseInputPin::PassNotify(Quality& q)

+{

+    // We pass the message on, which means that we find the quality sink

+    // for our input pin and send it there

+

+    DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));

+    if (m_pQSink!=NULL) {

+        return m_pQSink->Notify(m_pFilter, q);

+    } else {

+        // no sink set, so pass it upstream

+        HRESULT hr;

+        IQualityControl * pIQC;

+

+        hr = VFW_E_NOT_FOUND;                   // default

+        if (m_Connected) {

+            m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);

+

+            if (pIQC!=NULL) {

+                hr = pIQC->Notify(m_pFilter, q);

+                pIQC->Release();

+            }

+        }

+        return hr;

+    }

+

+} // PassNotify

+

+//=====================================================================

+//=====================================================================

+// Memory allocation class, implements CMediaSample

+//=====================================================================

+//=====================================================================

+

+

+/* NOTE The implementation of this class calls the CUnknown constructor with

+   a NULL outer unknown pointer. This has the effect of making us a self

+   contained class, ie any QueryInterface, AddRef or Release calls will be

+   routed to the class's NonDelegatingUnknown methods. You will typically

+   find that the classes that do this then override one or more of these

+   virtual functions to provide more specialised behaviour. A good example

+   of this is where a class wants to keep the QueryInterface internal but

+   still wants it's lifetime controlled by the external object */

+

+/* The last two parameters have default values of NULL and zero */

+

+CMediaSample::CMediaSample(__in_opt LPCTSTR pName,

+               __in_opt CBaseAllocator *pAllocator,

+               __inout_opt HRESULT *phr,

+               __in_bcount_opt(length) LPBYTE pBuffer,

+               LONG length) :

+    m_pBuffer(pBuffer),             // Initialise the buffer

+    m_cbBuffer(length),             // And it's length

+    m_lActual(length),              // By default, actual = length

+    m_pMediaType(NULL),             // No media type change

+    m_dwFlags(0),                   // Nothing set

+    m_cRef(0),                      // 0 ref count

+    m_dwTypeSpecificFlags(0),       // Type specific flags

+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id

+    m_pAllocator(pAllocator)        // Allocator

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    /* We must have an owner and it must also be derived from class

+       CBaseAllocator BUT we do not hold a reference count on it */

+

+    ASSERT(pAllocator);

+

+    if (length < 0) {

+        *phr = VFW_E_BUFFER_OVERFLOW;

+        m_cbBuffer = 0;

+    }

+}

+

+#ifdef UNICODE

+CMediaSample::CMediaSample(__in_opt LPCSTR pName,

+               __in_opt CBaseAllocator *pAllocator,

+               __inout_opt HRESULT *phr,

+               __in_bcount_opt(length) LPBYTE pBuffer,

+               LONG length) :

+    m_pBuffer(pBuffer),             // Initialise the buffer

+    m_cbBuffer(length),             // And it's length

+    m_lActual(length),              // By default, actual = length

+    m_pMediaType(NULL),             // No media type change

+    m_dwFlags(0),                   // Nothing set

+    m_cRef(0),                      // 0 ref count

+    m_dwTypeSpecificFlags(0),       // Type specific flags

+    m_dwStreamId(AM_STREAM_MEDIA),  // Stream id

+    m_pAllocator(pAllocator)        // Allocator

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    /* We must have an owner and it must also be derived from class

+       CBaseAllocator BUT we do not hold a reference count on it */

+

+    ASSERT(pAllocator);

+}

+#endif

+

+/* Destructor deletes the media type memory */

+

+CMediaSample::~CMediaSample()

+{

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );

+#endif // DXMPERF

+

+    if (m_pMediaType) {

+    DeleteMediaType(m_pMediaType);

+    }

+}

+

+/* Override this to publicise our interfaces */

+

+STDMETHODIMP

+CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    if (riid == IID_IMediaSample ||

+        riid == IID_IMediaSample2 ||

+        riid == IID_IUnknown) {

+        return GetInterface((IMediaSample *) this, ppv);

+    } else {

+        *ppv = NULL;

+        return E_NOINTERFACE;

+    }

+}

+

+STDMETHODIMP_(ULONG)

+CMediaSample::AddRef()

+{

+    return InterlockedIncrement(&m_cRef);

+}

+

+

+// --  CMediaSample lifetimes --

+//

+// On final release of this sample buffer it is not deleted but

+// returned to the freelist of the owning memory allocator

+//

+// The allocator may be waiting for the last buffer to be placed on the free

+// list in order to decommit all the memory, so the ReleaseBuffer() call may

+// result in this sample being deleted. We also need to hold a refcount on

+// the allocator to stop that going away until we have finished with this.

+// However, we cannot release the allocator before the ReleaseBuffer, as the

+// release may cause us to be deleted. Similarly we can't do it afterwards.

+//

+// Thus we must leave it to the allocator to hold an addref on our behalf.

+// When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer

+// is called, he releases himself, possibly causing us and him to be deleted.

+

+

+STDMETHODIMP_(ULONG)

+CMediaSample::Release()

+{

+    /* Decrement our own private reference count */

+    LONG lRef;

+    if (m_cRef == 1) {

+        lRef = 0;

+        m_cRef = 0;

+    } else {

+        lRef = InterlockedDecrement(&m_cRef);

+    }

+    ASSERT(lRef >= 0);

+

+    DbgLog((LOG_MEMORY,3,TEXT("    Unknown %X ref-- = %d"),

+        this, m_cRef));

+

+    /* Did we release our final reference count */

+    if (lRef == 0) {

+        /* Free all resources */

+        if (m_dwFlags & Sample_TypeChanged) {

+            SetMediaType(NULL);

+        }

+        ASSERT(m_pMediaType == NULL);

+        m_dwFlags = 0;

+        m_dwTypeSpecificFlags = 0;

+        m_dwStreamId = AM_STREAM_MEDIA;

+

+        /* This may cause us to be deleted */

+        // Our refcount is reliably 0 thus no-one will mess with us

+        m_pAllocator->ReleaseBuffer(this);

+    }

+    return (ULONG)lRef;

+}

+

+

+// set the buffer pointer and length. Used by allocators that

+// want variable sized pointers or pointers into already-read data.

+// This is only available through a CMediaSample* not an IMediaSample*

+// and so cannot be changed by clients.

+HRESULT

+CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)

+{

+    if (cBytes < 0) {

+        return VFW_E_BUFFER_OVERFLOW;

+    }

+    m_pBuffer = ptr;            // new buffer area (could be null)

+    m_cbBuffer = cBytes;        // length of buffer

+    m_lActual = cBytes;         // length of data in buffer (assume full)

+

+    return S_OK;

+}

+

+

+// get me a read/write pointer to this buffer's memory. I will actually

+// want to use sizeUsed bytes.

+STDMETHODIMP

+CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)

+{

+    ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));

+

+    // creator must have set pointer either during

+    // constructor or by SetPointer

+    ASSERT(m_pBuffer);

+

+    *ppBuffer = m_pBuffer;

+    return NOERROR;

+}

+

+

+// return the size in bytes of this buffer

+STDMETHODIMP_(LONG)

+CMediaSample::GetSize(void)

+{

+    return m_cbBuffer;

+}

+

+

+// get the stream time at which this sample should start and finish.

+STDMETHODIMP

+CMediaSample::GetTime(

+    __out REFERENCE_TIME * pTimeStart,     // put time here

+    __out REFERENCE_TIME * pTimeEnd

+)

+{

+    ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));

+    ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));

+

+    if (!(m_dwFlags & Sample_StopValid)) {

+        if (!(m_dwFlags & Sample_TimeValid)) {

+            return VFW_E_SAMPLE_TIME_NOT_SET;

+        } else {

+            *pTimeStart = m_Start;

+

+            //  Make sure old stuff works

+            *pTimeEnd = m_Start + 1;

+            return VFW_S_NO_STOP_TIME;

+        }

+    }

+

+    *pTimeStart = m_Start;

+    *pTimeEnd = m_End;

+    return NOERROR;

+}

+

+

+// Set the stream time at which this sample should start and finish.

+// NULL pointers means the time is reset

+STDMETHODIMP

+CMediaSample::SetTime(

+    __in_opt REFERENCE_TIME * pTimeStart,

+    __in_opt REFERENCE_TIME * pTimeEnd

+)

+{

+    if (pTimeStart == NULL) {

+        ASSERT(pTimeEnd == NULL);

+        m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);

+    } else {

+        if (pTimeEnd == NULL) {

+            m_Start = *pTimeStart;

+            m_dwFlags |= Sample_TimeValid;

+            m_dwFlags &= ~Sample_StopValid;

+        } else {

+            ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));

+            ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));

+            ASSERT(*pTimeEnd >= *pTimeStart);

+

+            m_Start = *pTimeStart;

+            m_End = *pTimeEnd;

+            m_dwFlags |= Sample_TimeValid | Sample_StopValid;

+        }

+    }

+    return NOERROR;

+}

+

+

+// get the media times (eg bytes) for this sample

+STDMETHODIMP

+CMediaSample::GetMediaTime(

+    __out LONGLONG * pTimeStart,

+    __out LONGLONG * pTimeEnd

+)

+{

+    ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));

+    ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));

+

+    if (!(m_dwFlags & Sample_MediaTimeValid)) {

+        return VFW_E_MEDIA_TIME_NOT_SET;

+    }

+

+    *pTimeStart = m_MediaStart;

+    *pTimeEnd = (m_MediaStart + m_MediaEnd);

+    return NOERROR;

+}

+

+

+// Set the media times for this sample

+STDMETHODIMP

+CMediaSample::SetMediaTime(

+    __in_opt LONGLONG * pTimeStart,

+    __in_opt LONGLONG * pTimeEnd

+)

+{

+    if (pTimeStart == NULL) {

+        ASSERT(pTimeEnd == NULL);

+        m_dwFlags &= ~Sample_MediaTimeValid;

+    } else {

+        if (NULL == pTimeEnd) {

+            return E_POINTER;

+        }

+        ValidateReadPtr(pTimeStart,sizeof(LONGLONG));

+        ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));

+        ASSERT(*pTimeEnd >= *pTimeStart);

+

+        m_MediaStart = *pTimeStart;

+        m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);

+        m_dwFlags |= Sample_MediaTimeValid;

+    }

+    return NOERROR;

+}

+

+

+STDMETHODIMP

+CMediaSample::IsSyncPoint(void)

+{

+    if (m_dwFlags & Sample_SyncPoint) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+

+STDMETHODIMP

+CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)

+{

+    if (bIsSyncPoint) {

+        m_dwFlags |= Sample_SyncPoint;

+    } else {

+        m_dwFlags &= ~Sample_SyncPoint;

+    }

+    return NOERROR;

+}

+

+// returns S_OK if there is a discontinuity in the data (this same is

+// not a continuation of the previous stream of data

+// - there has been a seek).

+STDMETHODIMP

+CMediaSample::IsDiscontinuity(void)

+{

+    if (m_dwFlags & Sample_Discontinuity) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+// set the discontinuity property - TRUE if this sample is not a

+// continuation, but a new sample after a seek.

+STDMETHODIMP

+CMediaSample::SetDiscontinuity(BOOL bDiscont)

+{

+    // should be TRUE or FALSE

+    if (bDiscont) {

+        m_dwFlags |= Sample_Discontinuity;

+    } else {

+        m_dwFlags &= ~Sample_Discontinuity;

+    }

+    return S_OK;

+}

+

+STDMETHODIMP

+CMediaSample::IsPreroll(void)

+{

+    if (m_dwFlags & Sample_Preroll) {

+        return S_OK;

+    } else {

+        return S_FALSE;

+    }

+}

+

+

+STDMETHODIMP

+CMediaSample::SetPreroll(BOOL bIsPreroll)

+{

+    if (bIsPreroll) {

+        m_dwFlags |= Sample_Preroll;

+    } else {

+        m_dwFlags &= ~Sample_Preroll;

+    }

+    return NOERROR;

+}

+

+STDMETHODIMP_(LONG)

+CMediaSample::GetActualDataLength(void)

+{

+    return m_lActual;

+}

+

+

+STDMETHODIMP

+CMediaSample::SetActualDataLength(LONG lActual)

+{

+    if (lActual > m_cbBuffer || lActual < 0) {

+        ASSERT(lActual <= GetSize());

+        return VFW_E_BUFFER_OVERFLOW;

+    }

+    m_lActual = lActual;

+    return NOERROR;

+}

+

+

+/* These allow for limited format changes in band */

+

+STDMETHODIMP

+CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)

+{

+    ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));

+    ASSERT(ppMediaType);

+

+    /* Do we have a new media type for them */

+

+    if (!(m_dwFlags & Sample_TypeChanged)) {

+        ASSERT(m_pMediaType == NULL);

+        *ppMediaType = NULL;

+        return S_FALSE;

+    }

+

+    ASSERT(m_pMediaType);

+

+    /* Create a copy of our media type */

+

+    *ppMediaType = CreateMediaType(m_pMediaType);

+    if (*ppMediaType == NULL) {

+        return E_OUTOFMEMORY;

+    }

+    return NOERROR;

+}

+

+

+/* Mark this sample as having a different format type */

+

+STDMETHODIMP

+CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)

+{

+    /* Delete the current media type */

+

+    if (m_pMediaType) {

+        DeleteMediaType(m_pMediaType);

+        m_pMediaType = NULL;

+    }

+

+    /* Mechanism for resetting the format type */

+

+    if (pMediaType == NULL) {

+        m_dwFlags &= ~Sample_TypeChanged;

+        return NOERROR;

+    }

+

+    ASSERT(pMediaType);

+    ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));

+

+    /* Take a copy of the media type */

+

+    m_pMediaType = CreateMediaType(pMediaType);

+    if (m_pMediaType == NULL) {

+        m_dwFlags &= ~Sample_TypeChanged;

+        return E_OUTOFMEMORY;

+    }

+

+    m_dwFlags |= Sample_TypeChanged;

+    return NOERROR;

+}

+

+// Set and get properties (IMediaSample2)

+STDMETHODIMP CMediaSample::GetProperties(

+    DWORD cbProperties,

+    __out_bcount(cbProperties) BYTE * pbProperties

+)

+{

+    if (0 != cbProperties) {

+        CheckPointer(pbProperties, E_POINTER);

+        //  Return generic stuff up to the length

+        AM_SAMPLE2_PROPERTIES Props;

+        Props.cbData     = min(cbProperties, sizeof(Props));

+        Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;

+        Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;

+        Props.pbBuffer   = m_pBuffer;

+        Props.cbBuffer   = m_cbBuffer;

+        Props.lActual    = m_lActual;

+        Props.tStart     = m_Start;

+        Props.tStop      = m_End;

+        Props.dwStreamId = m_dwStreamId;

+        if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {

+            Props.pMediaType = m_pMediaType;

+        } else {

+            Props.pMediaType = NULL;

+        }

+        CopyMemory(pbProperties, &Props, Props.cbData);

+    }

+    return S_OK;

+}

+

+#define CONTAINS_FIELD(type, field, offset) \

+    ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)

+

+HRESULT CMediaSample::SetProperties(

+    DWORD cbProperties,

+    __in_bcount(cbProperties) const BYTE * pbProperties

+)

+{

+

+    /*  Generic properties */

+    AM_MEDIA_TYPE *pMediaType = NULL;

+

+    if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {

+        CheckPointer(pbProperties, E_POINTER);

+        AM_SAMPLE2_PROPERTIES *pProps =

+            (AM_SAMPLE2_PROPERTIES *)pbProperties;

+

+        /*  Don't use more data than is actually there */

+        if (pProps->cbData < cbProperties) {

+            cbProperties = pProps->cbData;

+        }

+        /*  We only handle IMediaSample2 */

+        if (cbProperties > sizeof(*pProps) ||

+            pProps->cbData > sizeof(*pProps)) {

+            return E_INVALIDARG;

+        }

+        /*  Do checks first, the assignments (for backout) */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {

+            /*  Check the flags */

+            if (pProps->dwSampleFlags &

+                    (~Sample_ValidFlags | Sample_MediaTimeValid)) {

+                return E_INVALIDARG;

+            }

+            /*  Check a flag isn't being set for a property

+                not being provided

+            */

+            if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&

+                 !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&

+                 !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {

+                 return E_INVALIDARG;

+            }

+        }

+        /*  NB - can't SET the pointer or size */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {

+

+            /*  Check pbBuffer */

+            if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {

+                return E_INVALIDARG;

+            }

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {

+

+            /*  Check cbBuffer */

+            if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {

+                return E_INVALIDARG;

+            }

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&

+            CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {

+

+            /*  Check lActual */

+            if (pProps->cbBuffer < pProps->lActual) {

+                return E_INVALIDARG;

+            }

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {

+

+            /*  Check pMediaType */

+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {

+                CheckPointer(pProps->pMediaType, E_POINTER);

+                pMediaType = CreateMediaType(pProps->pMediaType);

+                if (pMediaType == NULL) {

+                    return E_OUTOFMEMORY;

+                }

+            }

+        }

+

+        /*  Now do the assignments */

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {

+            m_dwStreamId = pProps->dwStreamId;

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {

+            /*  Set the flags */

+            m_dwFlags = pProps->dwSampleFlags |

+                                (m_dwFlags & Sample_MediaTimeValid);

+            m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;

+        } else {

+            if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {

+                m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;

+            }

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {

+            /*  Set lActual */

+            m_lActual = pProps->lActual;

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {

+

+            /*  Set the times */

+            m_End   = pProps->tStop;

+        }

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {

+

+            /*  Set the times */

+            m_Start = pProps->tStart;

+        }

+

+        if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {

+            /*  Set pMediaType */

+            if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {

+                if (m_pMediaType != NULL) {

+                    DeleteMediaType(m_pMediaType);

+                }

+                m_pMediaType = pMediaType;

+            }

+        }

+

+        /*  Fix up the type changed flag to correctly reflect the current state

+            If, for instance the input contained no type change but the

+            output does then if we don't do this we'd lose the

+            output media type.

+        */

+        if (m_pMediaType) {

+            m_dwFlags |= Sample_TypeChanged;

+        } else {

+            m_dwFlags &= ~Sample_TypeChanged;

+        }

+    }

+

+    return S_OK;

+}

+

+

+//

+// The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),

+// IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the

+// connected input pin.  The application thread calls Block().  The

+// following class members can only be called by the streaming thread.

+//

+//    Deliver()

+//    DeliverNewSegment()

+//    StartUsingOutputPin()

+//    StopUsingOutputPin()

+//    ChangeOutputFormat()

+//    ChangeMediaType()

+//    DynamicReconnect()

+//

+// The following class members can only be called by the application thread.

+//

+//    Block()

+//    SynchronousBlockOutputPin()

+//    AsynchronousBlockOutputPin()

+//

+

+CDynamicOutputPin::CDynamicOutputPin(

+    __in_opt LPCTSTR pObjectName,

+    __in CBaseFilter *pFilter,

+    __in CCritSec *pLock,

+    __inout HRESULT *phr,

+    __in_opt LPCWSTR pName) :

+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),

+        m_hStopEvent(NULL),

+        m_pGraphConfig(NULL),

+        m_bPinUsesReadOnlyAllocator(FALSE),

+        m_BlockState(NOT_BLOCKED),

+        m_hUnblockOutputPinEvent(NULL),

+        m_hNotifyCallerPinBlockedEvent(NULL),

+        m_dwBlockCallerThreadID(0),

+        m_dwNumOutstandingOutputPinUsers(0)

+{

+    HRESULT hr = Initialize();

+    if( FAILED( hr ) ) {

+        *phr = hr;

+        return;

+    }

+}

+

+#ifdef UNICODE

+CDynamicOutputPin::CDynamicOutputPin(

+    __in_opt LPCSTR pObjectName,

+    __in CBaseFilter *pFilter,

+    __in CCritSec *pLock,

+    __inout HRESULT *phr,

+    __in_opt LPCWSTR pName) :

+        CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),

+        m_hStopEvent(NULL),

+        m_pGraphConfig(NULL),

+        m_bPinUsesReadOnlyAllocator(FALSE),

+        m_BlockState(NOT_BLOCKED),

+        m_hUnblockOutputPinEvent(NULL),

+        m_hNotifyCallerPinBlockedEvent(NULL),

+        m_dwBlockCallerThreadID(0),

+        m_dwNumOutstandingOutputPinUsers(0)

+{

+    HRESULT hr = Initialize();

+    if( FAILED( hr ) ) {

+        *phr = hr;

+        return;

+    }

+}

+#endif

+

+CDynamicOutputPin::~CDynamicOutputPin()

+{

+    if(NULL != m_hUnblockOutputPinEvent) {

+        // This call should not fail because we have access to m_hUnblockOutputPinEvent

+        // and m_hUnblockOutputPinEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));

+    }

+

+    if(NULL != m_hNotifyCallerPinBlockedEvent) {

+        // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent

+        // and m_hNotifyCallerPinBlockedEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+    }

+}

+

+HRESULT CDynamicOutputPin::Initialize(void)

+{

+    m_hUnblockOutputPinEvent = ::CreateEvent( NULL,   // The event will have the default security descriptor.

+                                              TRUE,   // This is a manual reset event.

+                                              TRUE,   // The event is initially signaled.

+                                              NULL ); // The event is not named.

+

+    // CreateEvent() returns NULL if an error occurs.

+    if(NULL == m_hUnblockOutputPinEvent) {

+        return AmGetLastErrorToHResult();

+    }

+

+    //  Set flag to say we can reconnect while streaming.

+    SetReconnectWhenActive(true);

+

+    return S_OK;

+}

+

+STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    if(riid == IID_IPinFlowControl) {

+        return GetInterface(static_cast<IPinFlowControl*>(this), ppv);

+    } else {

+        return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+STDMETHODIMP CDynamicOutputPin::Disconnect(void)

+{

+    CAutoLock cObjectLock(m_pLock);

+    return DisconnectInternal();

+}

+

+STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)

+{

+    const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;

+

+    // Check for illegal flags.

+    if(dwBlockFlags & ~VALID_FLAGS) {

+        return E_INVALIDARG;

+    }

+

+    // Make sure the event is unsignaled.

+    if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {

+        if( !::ResetEvent( hEvent ) ) {

+            return AmGetLastErrorToHResult();

+        }

+    }

+

+    // No flags are set if we are unblocking the output pin.

+    if(0 == dwBlockFlags) {

+

+        // This parameter should be NULL because unblock operations are always synchronous.

+        // There is no need to notify the caller when the event is done.

+        if(NULL != hEvent) {

+            return E_INVALIDARG;

+        }

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    HRESULT hr;

+

+    if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {

+        // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.

+        // If hEvent is not NULL, the block is asynchronous.

+        if(NULL == hEvent) {

+            hr = SynchronousBlockOutputPin();

+        } else {

+            hr = AsynchronousBlockOutputPin(hEvent);

+        }

+    } else {

+        hr = UnblockOutputPin();

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)

+{

+    HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL,   // The event will have the default security attributes.

+                                                          FALSE,  // This is an automatic reset event.

+                                                          FALSE,  // The event is initially unsignaled.

+                                                          NULL ); // The event is not named.

+

+    // CreateEvent() returns NULL if an error occurs.

+    if(NULL == hNotifyCallerPinBlockedEvent) {

+        return AmGetLastErrorToHResult();

+    }

+

+    HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);

+    if(FAILED(hr)) {

+        // This call should not fail because we have access to hNotifyCallerPinBlockedEvent

+        // and hNotifyCallerPinBlockedEvent is a valid event.

+        EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));

+

+        return hr;

+    }

+

+    hr = WaitEvent(hNotifyCallerPinBlockedEvent);

+

+    // This call should not fail because we have access to hNotifyCallerPinBlockedEvent

+    // and hNotifyCallerPinBlockedEvent is a valid event.

+    EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));

+

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)

+{

+    // This function holds the m_BlockStateLock because it uses

+    // m_dwBlockCallerThreadID, m_BlockState and

+    // m_hNotifyCallerPinBlockedEvent.

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    if(NOT_BLOCKED != m_BlockState) {

+        if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {

+            return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;

+        } else {

+            return VFW_E_PIN_ALREADY_BLOCKED;

+        }

+    }

+

+    BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),

+                                       hNotifyCallerPinBlockedEvent,

+                                       ::GetCurrentProcess(),

+                                       &m_hNotifyCallerPinBlockedEvent,

+                                       EVENT_MODIFY_STATE,

+                                       FALSE,

+                                       0 );

+    if( !fSuccess ) {

+        return AmGetLastErrorToHResult();

+    }

+

+    m_BlockState = PENDING;

+    m_dwBlockCallerThreadID = ::GetCurrentThreadId();

+

+    // The output pin cannot be blocked if the streaming thread is

+    // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()

+    // or IMemInputPin::ReceiveMultiple() on the connected input pin.  Also, it

+    // cannot be blocked if the streaming thread is calling DynamicReconnect(),

+    // ChangeMediaType() or ChangeOutputFormat().

+    if(!StreamingThreadUsingOutputPin()) {

+

+        // The output pin can be immediately blocked.

+        BlockOutputPin();

+    }

+

+    return S_OK;

+}

+

+void CDynamicOutputPin::BlockOutputPin(void)

+{

+    // The caller should always hold the m_BlockStateLock because this function

+    // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.

+    ASSERT(CritCheckIn(&m_BlockStateLock));

+

+    // This function should not be called if the streaming thread is modifying

+    // the connection state or it's passing data downstream.

+    ASSERT(!StreamingThreadUsingOutputPin());

+

+    // This should not fail because we successfully created the event

+    // and we have the security permissions to change it's state.

+    EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));

+

+    // This event should not fail because AsynchronousBlockOutputPin() successfully

+    // duplicated this handle and we have the appropriate security permissions.

+    EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));

+    EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+

+    m_BlockState = BLOCKED;

+    m_hNotifyCallerPinBlockedEvent = NULL;

+}

+

+HRESULT CDynamicOutputPin::UnblockOutputPin(void)

+{

+    // UnblockOutputPin() holds the m_BlockStateLock because it

+    // uses m_BlockState, m_dwBlockCallerThreadID and

+    // m_hNotifyCallerPinBlockedEvent.

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    if(NOT_BLOCKED == m_BlockState) {

+        return S_FALSE;

+    }

+

+    // This should not fail because we successfully created the event

+    // and we have the security permissions to change it's state.

+    EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));

+

+    // Cancel the block operation if it's still pending.

+    if(NULL != m_hNotifyCallerPinBlockedEvent) {

+        // This event should not fail because AsynchronousBlockOutputPin() successfully

+        // duplicated this handle and we have the appropriate security permissions.

+        EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));

+        EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));

+    }

+

+    m_BlockState = NOT_BLOCKED;

+    m_dwBlockCallerThreadID = 0;

+    m_hNotifyCallerPinBlockedEvent = NULL;

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::StartUsingOutputPin(void)

+{

+    // The caller should not hold m_BlockStateLock.  If the caller does,

+    // a deadlock could occur.

+    ASSERT(CritCheckOut(&m_BlockStateLock));

+

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    // Are we in the middle of a block operation?

+    while(BLOCKED == m_BlockState) {

+        m_BlockStateLock.Unlock();

+

+        // If this ASSERT fires, a deadlock could occur.  The caller should make sure

+        // that this thread never acquires the Block State lock more than once.

+        ASSERT(CritCheckOut( &m_BlockStateLock ));

+

+        // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event

+        // is fired.  It returns WAIT_OBJECT_0 + 1 if the stop event if fired.

+        // See the Windows SDK documentation for more information on

+        // WaitForMultipleObjects().

+        const DWORD UNBLOCK = WAIT_OBJECT_0;

+        const DWORD STOP = WAIT_OBJECT_0 + 1;

+

+        HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };

+        DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);

+

+        DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );

+

+        m_BlockStateLock.Lock();

+

+        #ifdef DEBUG

+        AssertValid();

+        #endif // DEBUG

+

+        switch( dwReturnValue ) {

+        case UNBLOCK:

+            break;

+

+        case STOP:

+            return VFW_E_STATE_CHANGED;

+

+        case WAIT_FAILED:

+            return AmGetLastErrorToHResult();

+

+        default:

+            DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );

+            return E_UNEXPECTED;

+        }

+    }

+

+    m_dwNumOutstandingOutputPinUsers++;

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    return S_OK;

+}

+

+void CDynamicOutputPin::StopUsingOutputPin(void)

+{

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+

+    m_dwNumOutstandingOutputPinUsers--;

+

+    if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {

+        BlockOutputPin();

+    }

+

+    #ifdef DEBUG

+    AssertValid();

+    #endif // DEBUG

+}

+

+bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)

+{

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    return (m_dwNumOutstandingOutputPinUsers > 0);

+}

+

+void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)

+{

+    // This pointer is not addrefed because filters are not allowed to

+    // hold references to the filter graph manager.  See the documentation for

+    // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.

+    m_pGraphConfig = pGraphConfig;

+

+    m_hStopEvent = hStopEvent;

+}

+

+HRESULT CDynamicOutputPin::Active(void)

+{

+    // Make sure the user initialized the object by calling SetConfigInfo().

+    if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {

+        DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized.  Call SetConfigInfo() to initialize them. );

+        return E_FAIL;

+    }

+

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event

+    // handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::Active();

+}

+

+HRESULT CDynamicOutputPin::Inactive(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then Active() is called.  An event

+    // handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::Inactive();

+}

+

+HRESULT CDynamicOutputPin::DeliverBeginFlush(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.

+    // An event handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(SetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::DeliverBeginFlush();

+}

+

+HRESULT CDynamicOutputPin::DeliverEndFlush(void)

+{

+    // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().

+    // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.

+    // An event handle is invalid if 1) the event does not exist or the user does not have the security

+    // permissions to use the event.

+    EXECUTE_ASSERT(ResetEvent(m_hStopEvent));

+

+    return CBaseOutputPin::DeliverEndFlush();

+}

+

+

+// ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly

+// reconnects the output pin.

+HRESULT CDynamicOutputPin::ChangeOutputFormat

+    (

+    const AM_MEDIA_TYPE *pmt,

+    REFERENCE_TIME tSegmentStart,

+    REFERENCE_TIME tSegmentStop,

+    double dSegmentRate

+    )

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    // Callers should always pass a valid media type to ChangeOutputFormat() .

+    ASSERT(NULL != pmt);

+

+    CMediaType cmt(*pmt);

+    HRESULT hr = ChangeMediaType(&cmt);

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);

+    if( FAILED( hr ) ) {

+        return hr;

+    }

+

+    return S_OK;

+}

+

+HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    // This function assumes the filter graph is running.

+    ASSERT(!IsStopped());

+

+    if(!IsConnected()) {

+        return VFW_E_NOT_CONNECTED;

+    }

+

+    /*  First check if the downstream pin will accept a dynamic

+        format change

+    */

+    QzCComPtr<IPinConnection> pConnection;

+

+    m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);

+    if(pConnection != NULL) {

+

+        if(S_OK == pConnection->DynamicQueryAccept(pmt)) {

+

+            HRESULT hr = ChangeMediaTypeHelper(pmt);

+            if(FAILED(hr)) {

+                return hr;

+            }

+

+            return S_OK;

+        }

+    }

+

+    /*  Can't do the dynamic connection */

+    return DynamicReconnect(pmt);

+}

+

+HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    HRESULT hr = m_Connected->ReceiveConnection(this, pmt);

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    hr = SetMediaType(pmt);

+    if(FAILED(hr)) {

+        return hr;

+    }

+

+    // Does this pin use the local memory transport?

+    if(NULL != m_pInputPin) {

+        // This function assumes that m_pInputPin and m_Connected are

+        // two different interfaces to the same object.

+        ASSERT(::IsEqualObject(m_Connected, m_pInputPin));

+

+        ALLOCATOR_PROPERTIES apInputPinRequirements;

+        apInputPinRequirements.cbAlign = 0;

+        apInputPinRequirements.cbBuffer = 0;

+        apInputPinRequirements.cbPrefix = 0;

+        apInputPinRequirements.cBuffers = 0;

+

+        m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);

+

+        // A zero allignment does not make any sense.

+        if(0 == apInputPinRequirements.cbAlign) {

+            apInputPinRequirements.cbAlign = 1;

+        }

+

+        hr = m_pAllocator->Decommit();

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = DecideBufferSize(m_pAllocator,  &apInputPinRequirements);

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = m_pAllocator->Commit();

+        if(FAILED(hr)) {

+            return hr;

+        }

+

+        hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);

+        if(FAILED(hr)) {

+            return hr;

+        }

+    }

+

+    return S_OK;

+}

+

+// this method has to be called from the thread that is pushing data,

+// and it's the caller's responsibility to make sure that the thread

+// has no outstand samples because they cannot be delivered after a

+// reconnect

+//

+HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )

+{

+    // The caller should call StartUsingOutputPin() before calling this

+    // method.

+    ASSERT(StreamingThreadUsingOutputPin());

+

+    if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {

+        return E_FAIL;

+    }

+

+    HRESULT hr = m_pGraphConfig->Reconnect(

+        this,

+        NULL,

+        pmt,

+        NULL,

+        m_hStopEvent,

+        AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );

+

+    return hr;

+}

+

+HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)

+{

+    HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);

+    if(SUCCEEDED(hr)) {

+        if(!IsStopped() && m_pAllocator) {

+            hr = m_pAllocator->Commit();

+            ASSERT(hr != VFW_E_ALREADY_COMMITTED);

+        }

+    }

+

+    return hr;

+}

+

+#ifdef DEBUG

+void CDynamicOutputPin::AssertValid(void)

+{

+    // Make sure the object was correctly initialized.

+

+    // This ASSERT only fires if the object failed to initialize

+    // and the user ignored the constructor's return code (phr).

+    ASSERT(NULL != m_hUnblockOutputPinEvent);

+

+    // If either of these ASSERTs fire, the user did not correctly call

+    // SetConfigInfo().

+    ASSERT(NULL != m_hStopEvent);

+    ASSERT(NULL != m_pGraphConfig);

+

+    // Make sure the block state is consistent.

+

+    CAutoLock alBlockStateLock(&m_BlockStateLock);

+

+    // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.

+    ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));

+

+    // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete

+    // immediately.

+    ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||

+           ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );

+

+    // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and

+    // the user is not trying to block the pin.

+    ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));

+

+    // If this ASSERT fires, the streaming thread is using the output pin and the

+    // output pin is blocked.

+    ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||

+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||

+           ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );

+}

+#endif // DEBUG

+

+HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)

+{

+    const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;

+

+    DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);

+

+    switch( dwReturnValue ) {

+    case EVENT_SIGNALED:

+        return S_OK;

+

+    case WAIT_FAILED:

+        return AmGetLastErrorToHResult();

+

+    default:

+        DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );

+        return E_UNEXPECTED;

+    }

+}

+

+//=====================================================================

+//=====================================================================

+// Implements CBaseAllocator

+//=====================================================================

+//=====================================================================

+

+

+/* Constructor overrides the default settings for the free list to request

+   that it be alertable (ie the list can be cast to a handle which can be

+   passed to WaitForSingleObject). Both of the allocator lists also ask for

+   object locking, the all list matches the object default settings but I

+   have included them here just so it is obvious what kind of list it is */

+

+CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,

+                               __inout_opt LPUNKNOWN pUnk,

+                               __inout HRESULT *phr,

+                               BOOL bEvent,

+                               BOOL fEnableReleaseCallback

+                               ) :

+    CUnknown(pName, pUnk),

+    m_lAllocated(0),

+    m_bChanged(FALSE),

+    m_bCommitted(FALSE),

+    m_bDecommitInProgress(FALSE),

+    m_lSize(0),

+    m_lCount(0),

+    m_lAlignment(0),

+    m_lPrefix(0),

+    m_hSem(NULL),

+    m_lWaiting(0),

+    m_fEnableReleaseCallback(fEnableReleaseCallback),

+    m_pNotify(NULL)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    if (bEvent) {

+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);

+        if (m_hSem == NULL) {

+            *phr = E_OUTOFMEMORY;

+            return;

+        }

+    }

+}

+

+#ifdef UNICODE

+CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,

+                               __inout_opt LPUNKNOWN pUnk,

+                               __inout HRESULT *phr,

+                               BOOL bEvent,

+                               BOOL fEnableReleaseCallback) :

+    CUnknown(pName, pUnk),

+    m_lAllocated(0),

+    m_bChanged(FALSE),

+    m_bCommitted(FALSE),

+    m_bDecommitInProgress(FALSE),

+    m_lSize(0),

+    m_lCount(0),

+    m_lAlignment(0),

+    m_lPrefix(0),

+    m_hSem(NULL),

+    m_lWaiting(0),

+    m_fEnableReleaseCallback(fEnableReleaseCallback),

+    m_pNotify(NULL)

+{

+#ifdef DXMPERF

+    PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    if (bEvent) {

+        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);

+        if (m_hSem == NULL) {

+            *phr = E_OUTOFMEMORY;

+            return;

+        }

+    }

+}

+#endif

+

+/* Destructor */

+

+CBaseAllocator::~CBaseAllocator()

+{

+    // we can't call Decommit here since that would mean a call to a

+    // pure virtual in destructor.

+    // We must assume that the derived class has gone into decommit state in

+    // its destructor.

+#ifdef DXMPERF

+    PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );

+#endif // DXMPERF

+

+    ASSERT(!m_bCommitted);

+    if (m_hSem != NULL) {

+        EXECUTE_ASSERT(CloseHandle(m_hSem));

+    }

+    if (m_pNotify) {

+        m_pNotify->Release();

+    }

+}

+

+

+/* Override this to publicise our interfaces */

+

+STDMETHODIMP

+CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)

+{

+    /* Do we know about this interface */

+

+    if (riid == IID_IMemAllocator ||

+        riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {

+        return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);

+    } else {

+        return CUnknown::NonDelegatingQueryInterface(riid, ppv);

+    }

+}

+

+

+/* This sets the size and count of the required samples. The memory isn't

+   actually allocated until Commit() is called, if memory has already been

+   allocated then assuming no samples are outstanding the user may call us

+   to change the buffering, the memory will be released in Commit() */

+

+STDMETHODIMP

+CBaseAllocator::SetProperties(

+                __in ALLOCATOR_PROPERTIES* pRequest,

+                __out ALLOCATOR_PROPERTIES* pActual)

+{

+    CheckPointer(pRequest, E_POINTER);

+    CheckPointer(pActual, E_POINTER);

+    ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));

+    CAutoLock cObjectLock(this);

+

+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));

+

+    ASSERT(pRequest->cbBuffer > 0);

+

+    /*  Check the alignment requested */

+    if (pRequest->cbAlign != 1) {

+        DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),

+               pRequest->cbAlign));

+        return VFW_E_BADALIGN;

+    }

+

+    /* Can't do this if already committed, there is an argument that says we

+       should not reject the SetProperties call if there are buffers still

+       active. However this is called by the source filter, which is the same

+       person who is holding the samples. Therefore it is not unreasonable

+       for them to free all their samples before changing the requirements */

+

+    if (m_bCommitted) {

+        return VFW_E_ALREADY_COMMITTED;

+    }

+

+    /* Must be no outstanding buffers */

+

+    if (m_lAllocated != m_lFree.GetCount()) {

+        return VFW_E_BUFFERS_OUTSTANDING;

+    }

+

+    /* There isn't any real need to check the parameters as they

+       will just be rejected when the user finally calls Commit */

+

+    pActual->cbBuffer = m_lSize = pRequest->cbBuffer;

+    pActual->cBuffers = m_lCount = pRequest->cBuffers;

+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;

+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;

+

+    m_bChanged = TRUE;

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBaseAllocator::GetProperties(

+    __out ALLOCATOR_PROPERTIES * pActual)

+{

+    CheckPointer(pActual,E_POINTER);

+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));

+

+    CAutoLock cObjectLock(this);

+    pActual->cbBuffer = m_lSize;

+    pActual->cBuffers = m_lCount;

+    pActual->cbAlign = m_lAlignment;

+    pActual->cbPrefix = m_lPrefix;

+    return NOERROR;

+}

+

+// get container for a sample. Blocking, synchronous call to get the

+// next free buffer (as represented by an IMediaSample interface).

+// on return, the time etc properties will be invalid, but the buffer

+// pointer and size will be correct.

+

+HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,

+                                  __in_opt REFERENCE_TIME *pStartTime,

+                                  __in_opt REFERENCE_TIME *pEndTime,

+                                  DWORD dwFlags

+                                  )

+{

+    UNREFERENCED_PARAMETER(pStartTime);

+    UNREFERENCED_PARAMETER(pEndTime);

+    UNREFERENCED_PARAMETER(dwFlags);

+    CMediaSample *pSample;

+

+    *ppBuffer = NULL;

+    for (;;)

+    {

+        {  // scope for lock

+            CAutoLock cObjectLock(this);

+

+            /* Check we are committed */

+            if (!m_bCommitted) {

+                return VFW_E_NOT_COMMITTED;

+            }

+            pSample = (CMediaSample *) m_lFree.RemoveHead();

+            if (pSample == NULL) {

+                SetWaiting();

+            }

+        }

+

+        /* If we didn't get a sample then wait for the list to signal */

+

+        if (pSample) {

+            break;

+        }

+        if (dwFlags & AM_GBF_NOWAIT) {

+            return VFW_E_TIMEOUT;

+        }

+        ASSERT(m_hSem != NULL);

+        WaitForSingleObject(m_hSem, INFINITE);

+    }

+

+    /* Addref the buffer up to one. On release

+       back to zero instead of being deleted, it will requeue itself by

+       calling the ReleaseBuffer member function. NOTE the owner of a

+       media sample must always be derived from CBaseAllocator */

+

+

+    ASSERT(pSample->m_cRef == 0);

+    pSample->m_cRef = 1;

+    *ppBuffer = pSample;

+

+#ifdef DXMPERF

+    PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );

+#endif // DXMPERF

+

+    return NOERROR;

+}

+

+

+/* Final release of a CMediaSample will call this */

+

+STDMETHODIMP

+CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)

+{

+    CheckPointer(pSample,E_POINTER);

+    ValidateReadPtr(pSample,sizeof(IMediaSample));

+

+#ifdef DXMPERF

+    PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );

+#endif // DXMPERF

+

+

+    BOOL bRelease = FALSE;

+    {

+        CAutoLock cal(this);

+

+        /* Put back on the free list */

+

+        m_lFree.Add((CMediaSample *)pSample);

+        if (m_lWaiting != 0) {

+            NotifySample();

+        }

+

+        // if there is a pending Decommit, then we need to complete it by

+        // calling Free() when the last buffer is placed on the free list

+

+        LONG l1 = m_lFree.GetCount();

+        if (m_bDecommitInProgress && (l1 == m_lAllocated)) {

+            Free();

+            m_bDecommitInProgress = FALSE;

+            bRelease = TRUE;

+        }

+    }

+

+    if (m_pNotify) {

+

+        ASSERT(m_fEnableReleaseCallback);

+

+        //

+        // Note that this is not synchronized with setting up a notification

+        // method.

+        //

+        m_pNotify->NotifyRelease();

+    }

+

+    /* For each buffer there is one AddRef, made in GetBuffer and released

+       here. This may cause the allocator and all samples to be deleted */

+

+    if (bRelease) {

+        Release();

+    }

+    return NOERROR;

+}

+

+STDMETHODIMP

+CBaseAllocator::SetNotify(

+    IMemAllocatorNotifyCallbackTemp* pNotify

+    )

+{

+    ASSERT(m_fEnableReleaseCallback);

+    CAutoLock lck(this);

+    if (pNotify) {

+        pNotify->AddRef();

+    }

+    if (m_pNotify) {

+        m_pNotify->Release();

+    }

+    m_pNotify = pNotify;

+    return S_OK;

+}

+

+STDMETHODIMP

+CBaseAllocator::GetFreeCount(

+    __out LONG* plBuffersFree

+    )

+{

+    ASSERT(m_fEnableReleaseCallback);

+    CAutoLock cObjectLock(this);

+    *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();

+    return NOERROR;

+}

+

+void

+CBaseAllocator::NotifySample()

+{

+    if (m_lWaiting != 0) {

+        ASSERT(m_hSem != NULL);

+        ReleaseSemaphore(m_hSem, m_lWaiting, 0);

+        m_lWaiting = 0;

+    }

+}

+

+STDMETHODIMP

+CBaseAllocator::Commit()

+{

+    /* Check we are not decommitted */

+    CAutoLock cObjectLock(this);

+

+    // cannot need to alloc or re-alloc if we are committed

+    if (m_bCommitted) {

+        return NOERROR;

+    }

+

+    /* Allow GetBuffer calls */

+

+    m_bCommitted = TRUE;

+

+    // is there a pending decommit ? if so, just cancel it

+    if (m_bDecommitInProgress) {

+        m_bDecommitInProgress = FALSE;

+

+        // don't call Alloc at this point. He cannot allow SetProperties

+        // between Decommit and the last free, so the buffer size cannot have

+        // changed. And because some of the buffers are not free yet, he

+        // cannot re-alloc anyway.

+        return NOERROR;

+    }

+

+    DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));

+

+    // actually need to allocate the samples

+    HRESULT hr = Alloc();

+    if (FAILED(hr)) {

+        m_bCommitted = FALSE;

+        return hr;

+    }

+    AddRef();

+    return NOERROR;

+}

+

+

+STDMETHODIMP

+CBaseAllocator::Decommit()

+{

+    BOOL bRelease = FALSE;

+    {

+        /* Check we are not already decommitted */

+        CAutoLock cObjectLock(this);

+        if (m_bCommitted == FALSE) {

+            if (m_bDecommitInProgress == FALSE) {

+                return NOERROR;

+            }

+        }

+

+        /* No more GetBuffer calls will succeed */

+        m_bCommitted = FALSE;

+

+        // are any buffers outstanding?

+        if (m_lFree.GetCount() < m_lAllocated) {

+            // please complete the decommit when last buffer is freed

+            m_bDecommitInProgress = TRUE;

+        } else {

+            m_bDecommitInProgress = FALSE;

+

+            // need to complete the decommit here as there are no

+            // outstanding buffers

+

+            Free();

+            bRelease = TRUE;

+        }

+

+        // Tell anyone waiting that they can go now so we can

+        // reject their call

+#pragma warning(push)

+#ifndef _PREFAST_

+#pragma warning(disable:4068)

+#endif

+#pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")

+        NotifySample();

+

+#pragma warning(pop)

+    }

+

+    if (bRelease) {

+        Release();

+    }

+    return NOERROR;

+}

+

+

+/* Base definition of allocation which checks we are ok to go ahead and do

+   the full allocation. We return S_FALSE if the requirements are the same */

+

+HRESULT

+CBaseAllocator::Alloc(void)

+{

+    /* Error if he hasn't set the size yet */

+    if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {

+        return VFW_E_SIZENOTSET;

+    }

+

+    /* should never get here while buffers outstanding */

+    ASSERT(m_lFree.GetCount() == m_lAllocated);

+

+    /* If the requirements haven't changed then don't reallocate */

+    if (m_bChanged == FALSE) {

+        return S_FALSE;

+    }

+

+    return NOERROR;

+}

+

+/*  Implement CBaseAllocator::CSampleList::Remove(pSample)

+    Removes pSample from the list

+*/

+void

+CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)

+{

+    CMediaSample **pSearch;

+    for (pSearch = &m_List;

+         *pSearch != NULL;

+         pSearch = &(CBaseAllocator::NextSample(*pSearch))) {

+       if (*pSearch == pSample) {

+           *pSearch = CBaseAllocator::NextSample(pSample);

+           CBaseAllocator::NextSample(pSample) = NULL;

+           m_nOnList--;

+           return;

+       }

+    }

+    DbgBreak("Couldn't find sample in list");

+}

+

+//=====================================================================

+//=====================================================================

+// Implements CMemAllocator

+//=====================================================================

+//=====================================================================

+

+

+/* This goes in the factory template table to create new instances */

+CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)

+{

+    CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);

+    return pUnkRet;

+}

+

+CMemAllocator::CMemAllocator(

+    __in_opt LPCTSTR pName,

+    __inout_opt LPUNKNOWN pUnk,

+    __inout HRESULT *phr)

+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),

+    m_pBuffer(NULL)

+{

+}

+

+#ifdef UNICODE

+CMemAllocator::CMemAllocator(

+    __in_opt LPCSTR pName,

+    __inout_opt LPUNKNOWN pUnk,

+    __inout HRESULT *phr)

+    : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),

+    m_pBuffer(NULL)

+{

+}

+#endif

+

+/* This sets the size and count of the required samples. The memory isn't

+   actually allocated until Commit() is called, if memory has already been

+   allocated then assuming no samples are outstanding the user may call us

+   to change the buffering, the memory will be released in Commit() */

+STDMETHODIMP

+CMemAllocator::SetProperties(

+                __in ALLOCATOR_PROPERTIES* pRequest,

+                __out ALLOCATOR_PROPERTIES* pActual)

+{

+    CheckPointer(pActual,E_POINTER);

+    ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));

+    CAutoLock cObjectLock(this);

+

+    ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));

+

+    ASSERT(pRequest->cbBuffer > 0);

+

+    SYSTEM_INFO SysInfo;

+    GetSystemInfo(&SysInfo);

+

+    /*  Check the alignment request is a power of 2 */

+    if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {

+        DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),

+               pRequest->cbAlign));

+    }

+    /*  Check the alignment requested */

+    if (pRequest->cbAlign == 0 ||

+    (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {

+        DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),

+               pRequest->cbAlign, SysInfo.dwAllocationGranularity));

+        return VFW_E_BADALIGN;

+    }

+

+    /* Can't do this if already committed, there is an argument that says we

+       should not reject the SetProperties call if there are buffers still

+       active. However this is called by the source filter, which is the same

+       person who is holding the samples. Therefore it is not unreasonable

+       for them to free all their samples before changing the requirements */

+

+    if (m_bCommitted == TRUE) {

+        return VFW_E_ALREADY_COMMITTED;

+    }

+

+    /* Must be no outstanding buffers */

+

+    if (m_lFree.GetCount() < m_lAllocated) {

+        return VFW_E_BUFFERS_OUTSTANDING;

+    }

+

+    /* There isn't any real need to check the parameters as they

+       will just be rejected when the user finally calls Commit */

+

+    // round length up to alignment - remember that prefix is included in

+    // the alignment

+    LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;

+    LONG lRemainder = lSize % pRequest->cbAlign;

+    if (lRemainder != 0) {

+        lSize = lSize - lRemainder + pRequest->cbAlign;

+    }

+    pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);

+

+    pActual->cBuffers = m_lCount = pRequest->cBuffers;

+    pActual->cbAlign = m_lAlignment = pRequest->cbAlign;

+    pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;

+

+    m_bChanged = TRUE;

+    return NOERROR;

+}

+

+// override this to allocate our resources when Commit is called.

+//

+// note that our resources may be already allocated when this is called,

+// since we don't free them on Decommit. We will only be called when in

+// decommit state with all buffers free.

+//

+// object locked by caller

+HRESULT

+CMemAllocator::Alloc(void)

+{

+    CAutoLock lck(this);

+

+    /* Check he has called SetProperties */

+    HRESULT hr = CBaseAllocator::Alloc();

+    if (FAILED(hr)) {

+        return hr;

+    }

+

+    /* If the requirements haven't changed then don't reallocate */

+    if (hr == S_FALSE) {

+        ASSERT(m_pBuffer);

+        return NOERROR;

+    }

+    ASSERT(hr == S_OK); // we use this fact in the loop below

+

+    /* Free the old resources */

+    if (m_pBuffer) {

+        ReallyFree();

+    }

+

+    /* Make sure we've got reasonable values */

+    if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {

+        return E_OUTOFMEMORY;

+    }

+

+    /* Compute the aligned size */

+    LONG lAlignedSize = m_lSize + m_lPrefix;

+

+    /*  Check overflow */

+    if (lAlignedSize < m_lSize) {

+        return E_OUTOFMEMORY;

+    }

+

+    if (m_lAlignment > 1) {

+        LONG lRemainder = lAlignedSize % m_lAlignment;

+        if (lRemainder != 0) {

+            LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;

+            if (lNewSize < lAlignedSize) {

+                return E_OUTOFMEMORY;

+            }

+            lAlignedSize = lNewSize;

+        }

+    }

+

+    /* Create the contiguous memory block for the samples

+       making sure it's properly aligned (64K should be enough!)

+    */

+    ASSERT(lAlignedSize % m_lAlignment == 0);

+

+    LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;

+

+    /*  Check overflow */

+    if (lToAllocate > MAXLONG) {

+        return E_OUTOFMEMORY;

+    }

+

+    m_pBuffer = (PBYTE)VirtualAlloc(NULL,

+                    (LONG)lToAllocate,

+                    MEM_COMMIT,

+                    PAGE_READWRITE);

+

+    if (m_pBuffer == NULL) {

+        return E_OUTOFMEMORY;

+    }

+

+    LPBYTE pNext = m_pBuffer;

+    CMediaSample *pSample;

+

+    ASSERT(m_lAllocated == 0);

+

+    // Create the new samples - we have allocated m_lSize bytes for each sample

+    // plus m_lPrefix bytes per sample as a prefix. We set the pointer to

+    // the memory after the prefix - so that GetPointer() will return a pointer

+    // to m_lSize bytes.

+    for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {

+

+

+        pSample = new CMediaSample(

+                            NAME("Default memory media sample"),

+                this,

+                            &hr,

+                            pNext + m_lPrefix,      // GetPointer() value

+                            m_lSize);               // not including prefix

+

+            ASSERT(SUCCEEDED(hr));

+        if (pSample == NULL) {

+            return E_OUTOFMEMORY;

+        }

+

+        // This CANNOT fail

+        m_lFree.Add(pSample);

+    }

+

+    m_bChanged = FALSE;

+    return NOERROR;

+}

+

+

+// override this to free up any resources we have allocated.

+// called from the base class on Decommit when all buffers have been

+// returned to the free list.

+//

+// caller has already locked the object.

+

+// in our case, we keep the memory until we are deleted, so

+// we do nothing here. The memory is deleted in the destructor by

+// calling ReallyFree()

+void

+CMemAllocator::Free(void)

+{

+    return;

+}

+

+

+// called from the destructor (and from Alloc if changing size/count) to

+// actually free up the memory

+void

+CMemAllocator::ReallyFree(void)

+{

+    /* Should never be deleting this unless all buffers are freed */

+

+    ASSERT(m_lAllocated == m_lFree.GetCount());

+

+    /* Free up all the CMediaSamples */

+

+    CMediaSample *pSample;

+    for (;;) {

+        pSample = m_lFree.RemoveHead();

+        if (pSample != NULL) {

+            delete pSample;

+        } else {

+            break;

+        }

+    }

+

+    m_lAllocated = 0;

+

+    // free the block of buffer memory

+    if (m_pBuffer) {

+        EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));

+        m_pBuffer = NULL;

+    }

+}

+

+

+/* Destructor frees our memory resources */

+

+CMemAllocator::~CMemAllocator()

+{

+    Decommit();

+    ReallyFree();

+}

+

+// ------------------------------------------------------------------------

+// filter registration through IFilterMapper. used if IFilterMapper is

+// not found (Quartz 1.0 install)

+

+STDAPI

+AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata

+                         , IFilterMapper *                  pIFM

+                         , BOOL                             bRegister  )

+{

+  DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));

+

+  // check we've got data

+  //

+  if( NULL == psetupdata ) return S_FALSE;

+

+

+  // unregister filter

+  // (as pins are subkeys of filter's CLSID key

+  // they do not need to be removed separately).

+  //

+  DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));

+  HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );

+

+

+  if( bRegister )

+  {

+    // register filter

+    //

+    DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));

+    hr = pIFM->RegisterFilter( *(psetupdata->clsID)

+                             , psetupdata->strName

+                             , psetupdata->dwMerit    );

+    if( SUCCEEDED(hr) )

+    {

+      // all its pins

+      //

+      DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));

+      for( UINT m1=0; m1 < psetupdata->nPins; m1++ )

+      {

+        hr = pIFM->RegisterPin( *(psetupdata->clsID)

+                              , psetupdata->lpPin[m1].strName

+                              , psetupdata->lpPin[m1].bRendered

+                              , psetupdata->lpPin[m1].bOutput

+                              , psetupdata->lpPin[m1].bZero

+                              , psetupdata->lpPin[m1].bMany

+                              , *(psetupdata->lpPin[m1].clsConnectsToFilter)

+                              , psetupdata->lpPin[m1].strConnectsToPin );

+

+        if( SUCCEEDED(hr) )

+        {

+          // and each pin's media types

+          //

+          DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));

+          for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )

+          {

+            hr = pIFM->RegisterPinType( *(psetupdata->clsID)

+                                      , psetupdata->lpPin[m1].strName

+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)

+                                      , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );

+            if( FAILED(hr) ) break;

+          }

+          if( FAILED(hr) ) break;

+        }

+        if( FAILED(hr) ) break;

+      }

+    }

+  }

+

+  // handle one acceptable "error" - that

+  // of filter not being registered!

+  // (couldn't find a suitable #define'd

+  // name for the error!)

+  //

+  if( 0x80070002 == hr)

+    return NOERROR;

+  else

+    return hr;

+}

+

+//  Remove warnings about unreferenced inline functions

+#pragma warning(disable:4514)

+

+#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */

diff --git a/jni/pjproject-android/.svn/pristine/21/21bfdda21634c7938f5895d9daa1566b4115313d.svn-base b/jni/pjproject-android/.svn/pristine/21/21bfdda21634c7938f5895d9daa1566b4115313d.svn-base
new file mode 100644
index 0000000..4b1d61b
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21bfdda21634c7938f5895d9daa1566b4115313d.svn-base
@@ -0,0 +1,475 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/lock.h>
+
+#include "os_symbian.h"
+
+
+#define DEFAULT_MAX_TIMED_OUT_PER_POLL  (64)
+
+// Maximum number of miliseconds that RTimer.At() supports
+#define MAX_RTIMER_INTERVAL		2147
+
+/* Absolute maximum number of timer entries */
+#ifndef PJ_SYMBIAN_TIMER_MAX_COUNT
+#  define PJ_SYMBIAN_TIMER_MAX_COUNT	65535
+#endif
+
+/* Get the number of free slots in the timer heap */
+#define FREECNT(th)	(th->max_size - th->cur_size)
+
+// Forward declaration
+class CPjTimerEntry;
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+    /** Maximum size of the heap. */
+    pj_size_t max_size;
+
+    /** Current size of the heap. */
+    pj_size_t cur_size;
+
+    /** Array of timer entries. A scheduled timer will occupy one slot, and
+     *  the slot number will be saved in entry->_timer_id
+     */
+    CPjTimerEntry **entries;
+    
+    /** Array of free slot indexes in the "entries" array */
+    int *free_slots;
+};
+
+/**
+ * Active object for each timer entry.
+ */
+class CPjTimerEntry : public CActive 
+{
+public:
+    pj_timer_entry  *entry_;
+    
+    static CPjTimerEntry* NewL(	pj_timer_heap_t *timer_heap,
+    				pj_timer_entry *entry,
+    				const pj_time_val *delay);
+    
+    ~CPjTimerEntry();
+    
+    virtual void RunL();
+    virtual void DoCancel();
+
+private:	
+    pj_timer_heap_t *timer_heap_;
+    RTimer	     rtimer_;
+    pj_uint32_t	     interval_left_;
+    
+    CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
+    void ConstructL(const pj_time_val *delay);
+    void Schedule();
+};
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * Implementation.
+ */
+
+/* Grow timer heap to the specified size */
+static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size)
+{
+    typedef CPjTimerEntry *entry_ptr;
+    CPjTimerEntry **entries = NULL;
+    int *free_slots = NULL;
+    unsigned i, j;
+ 
+    if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) {
+	/* Just some sanity limit */
+	new_size = PJ_SYMBIAN_TIMER_MAX_COUNT;
+	if (new_size <= th->max_size) {
+	    /* We've grown large enough */
+	    pj_assert(!"Too many timer heap entries");
+	    return PJ_ETOOMANY;
+	}
+    }
+    
+    /* Allocate entries, move entries from the old array if there is one */
+    entries = new entry_ptr[new_size];
+    if (th->entries) {
+	pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0]));
+    }
+    /* Initialize the remaining new area */
+    pj_bzero(&entries[th->max_size], 
+	    (new_size - th->max_size) * sizeof(th->entries[0]));
+    
+    /* Allocate free slots array */
+    free_slots = new int[new_size];
+    if (th->free_slots) {
+	pj_memcpy(free_slots, th->free_slots, 
+		  FREECNT(th) * sizeof(th->free_slots[0]));
+    }
+    /* Initialize the remaining new area */
+    for (i=FREECNT(th), j=th->max_size; j<new_size; ++i, ++j) {
+	free_slots[i] = j;
+    }
+    for ( ; i<new_size; ++i) {
+	free_slots[i] = -1;
+    }
+    
+    /* Apply */
+    delete [] th->entries;
+    th->entries = entries;
+    th->max_size = new_size;
+    delete [] th->free_slots;
+    th->free_slots = free_slots;
+
+    return PJ_SUCCESS;
+}
+
+/* Allocate and register an entry to timer heap for newly scheduled entry */
+static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
+{
+    pj_status_t status;
+    int slot;
+    
+    /* Check that there's still capacity left in the timer heap */
+    if (FREECNT(th) < 1) {
+	// Grow the timer heap twice the capacity
+	status = realloc_timer_heap(th, th->max_size * 2);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+    
+    /* Allocate one free slot. Use LIFO */
+    slot = th->free_slots[FREECNT(th)-1];
+    PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) && 
+		     (th->entries[slot]==NULL), PJ_EBUG);
+    
+    th->free_slots[FREECNT(th)-1] = -1;
+    th->entries[slot] = entry;
+    entry->entry_->_timer_id = slot;
+    ++th->cur_size;
+    
+    return PJ_SUCCESS;
+}
+
+/* Free a slot when an entry's timer has elapsed or cancel */
+static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
+{
+    int slot = entry->entry_->_timer_id;
+    
+    PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG);
+    PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG);
+    PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG);
+    PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG);
+    
+    th->entries[slot] = NULL;
+    th->free_slots[FREECNT(th)] = slot;
+    entry->entry_->_timer_id = -1;
+    --th->cur_size;
+    
+    return PJ_SUCCESS;
+}
+
+
+CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap,
+			     pj_timer_entry *entry)
+: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap), 
+  interval_left_(0)
+{
+}
+
+CPjTimerEntry::~CPjTimerEntry() 
+{
+    Cancel();
+    rtimer_.Close();
+}
+
+void CPjTimerEntry::Schedule()
+{
+    pj_int32_t interval;
+    
+    if (interval_left_ > MAX_RTIMER_INTERVAL) {
+	interval = MAX_RTIMER_INTERVAL;
+    } else {
+	interval = interval_left_;
+    }
+    
+    interval_left_ -= interval;
+    rtimer_.After(iStatus, interval * 1000);
+    SetActive();
+}
+
+void CPjTimerEntry::ConstructL(const pj_time_val *delay) 
+{
+    rtimer_.CreateLocal();
+    CActiveScheduler::Add(this);
+    
+    interval_left_ = PJ_TIME_VAL_MSEC(*delay);
+    Schedule();
+}
+
+CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap,
+				   pj_timer_entry *entry,
+				   const pj_time_val *delay) 
+{
+    CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry);
+    CleanupStack::PushL(self);
+    self->ConstructL(delay);
+    CleanupStack::Pop(self);
+
+    return self;
+}
+
+void CPjTimerEntry::RunL() 
+{
+    if (interval_left_ > 0) {
+	Schedule();
+	return;
+    }
+    
+    remove_entry(timer_heap_, this);
+    entry_->cb(timer_heap_, entry_);
+    
+    // Finger's crossed!
+    delete this;
+}
+
+void CPjTimerEntry::DoCancel() 
+{
+    /* It's possible that _timer_id is -1, see schedule(). In this case,
+     * the entry has not been added to the timer heap, so don't remove
+     * it.
+     */
+    if (entry_ && entry_->_timer_id != -1)
+	remove_entry(timer_heap_, this);
+    
+    rtimer_.Cancel();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+/*
+ * Calculate memory size required to create a timer heap.
+ */
+PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
+{
+    return /* size of the timer heap itself: */
+           sizeof(pj_timer_heap_t) + 
+           /* size of each entry: */
+           (count+2) * (sizeof(void*)+sizeof(int)) +
+           /* lock, pool etc: */
+           132;
+}
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+					  pj_size_t size,
+                                          pj_timer_heap_t **p_heap)
+{
+    pj_timer_heap_t *ht;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
+
+    *p_heap = NULL;
+
+    /* Allocate timer heap data structure from the pool */
+    ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
+    if (!ht)
+        return PJ_ENOMEM;
+
+    /* Allocate slots */
+    status = realloc_timer_heap(ht, size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *p_heap = ht;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
+{
+    /* Cancel and delete pending active objects */
+    if (ht->entries) {
+	unsigned i;
+	for (i=0; i<ht->max_size; ++i) {
+	    if (ht->entries[i]) {
+		ht->entries[i]->entry_ = NULL;
+		ht->entries[i]->Cancel();
+		delete ht->entries[i];
+		ht->entries[i] = NULL;
+	    }
+	}
+    }
+    
+    delete [] ht->entries;
+    delete [] ht->free_slots;
+    
+    ht->entries = NULL;
+    ht->free_slots = NULL;
+}
+
+PJ_DEF(void) pj_timer_heap_set_lock(  pj_timer_heap_t *ht,
+                                      pj_lock_t *lock,
+                                      pj_bool_t auto_del )
+{
+    PJ_UNUSED_ARG(ht);
+    if (auto_del)
+    	pj_lock_destroy(lock);
+}
+
+
+PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
+                                                          unsigned count )
+{
+    /* Not applicable */
+    PJ_UNUSED_ARG(count);
+    return ht->max_size;
+}
+
+PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+                                             int id,
+                                             void *user_data,
+                                             pj_timer_heap_callback *cb )
+{
+    pj_assert(entry && cb);
+
+    entry->_timer_id = -1;
+    entry->id = id;
+    entry->user_data = user_data;
+    entry->cb = cb;
+
+    return entry;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+					    pj_timer_entry *entry, 
+					    const pj_time_val *delay)
+{
+    CPjTimerEntry *timerObj;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
+    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
+
+    /* Prevent same entry from being scheduled more than once */
+    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
+
+    entry->_timer_id = -1;
+    
+    timerObj = CPjTimerEntry::NewL(ht, entry, delay);
+    status = add_entry(ht, timerObj);
+    if (status != PJ_SUCCESS) {
+	timerObj->Cancel();
+	delete timerObj;
+	return status;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
+                                                      pj_timer_entry *entry,
+                                                      const pj_time_val *delay,
+                                                      int id_val,
+                                                      pj_grp_lock_t *grp_lock)
+{
+    pj_status_t status;
+	    
+    PJ_UNUSED_ARG(grp_lock);
+
+    status = pj_timer_heap_schedule(ht, entry, delay);
+    
+    if (status == PJ_SUCCESS)
+    	entry->id = id_val;
+    
+    return status;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				  pj_timer_entry *entry)
+{
+    PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
+    
+    if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) {
+    	CPjTimerEntry *timerObj = ht->entries[entry->_timer_id];
+    	if (timerObj) {
+    	    timerObj->Cancel();
+    	    delete timerObj;
+    	    return 1;
+    	} else {
+    	    return 0;
+    	}
+    } else {
+    	return 0;
+    }
+}
+
+PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+                                           pj_timer_entry *entry,
+                                           int id_val)
+{
+    int count = pj_timer_heap_cancel(ht, entry);
+    if (count == 1)
+    	entry->id = id_val;
+    
+    return count;
+}
+
+PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
+                                     pj_time_val *next_delay )
+{
+    /* Polling is not necessary on Symbian, since all async activities
+     * are registered to active scheduler.
+     */
+    PJ_UNUSED_ARG(ht);
+    if (next_delay) {
+    	next_delay->sec = 1;
+    	next_delay->msec = 0;
+    }
+    return 0;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+{
+    PJ_ASSERT_RETURN(ht, 0);
+
+    return ht->cur_size;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+					         pj_time_val *timeval)
+{
+    /* We don't support this! */
+    PJ_UNUSED_ARG(ht);
+    
+    timeval->sec = 1;
+    timeval->msec = 0;
+    
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/21/21d750c92f9d07cf23ab09c5f058ae7784747af8.svn-base b/jni/pjproject-android/.svn/pristine/21/21d750c92f9d07cf23ab09c5f058ae7784747af8.svn-base
new file mode 100644
index 0000000..9614367
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21d750c92f9d07cf23ab09c5f058ae7784747af8.svn-base
@@ -0,0 +1 @@
+#include <pjsua-lib/pjsua.h>
diff --git a/jni/pjproject-android/.svn/pristine/21/21fe66004a48c6cbe5fcd23f0f559251886ecbe5.svn-base b/jni/pjproject-android/.svn/pristine/21/21fe66004a48c6cbe5fcd23f0f559251886ecbe5.svn-base
new file mode 100644
index 0000000..295b5f6
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21fe66004a48c6cbe5fcd23f0f559251886ecbe5.svn-base
@@ -0,0 +1,159 @@
+/*
+ * auth.h
+ *
+ * common interface to authentication functions
+ *
+ * David A. McGrew
+ * Cisco Systems, Inc.
+ */
+
+/*
+ *	
+ * Copyright (c) 2001-2006, Cisco Systems, Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 
+ *   Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * 
+ *   Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ * 
+ *   Neither the name of the Cisco Systems, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef AUTH_H
+#define AUTH_H
+
+#include "datatypes.h"          
+#include "err.h"                /* error codes    */
+
+typedef struct auth_type_t *auth_type_pointer;
+typedef struct auth_t      *auth_pointer_t;
+
+typedef err_status_t (*auth_alloc_func)
+     (auth_pointer_t *ap, int key_len, int out_len);
+
+typedef err_status_t (*auth_init_func)
+     (void *state, const uint8_t *key, int key_len);
+
+typedef err_status_t (*auth_dealloc_func)(auth_pointer_t ap);
+
+typedef err_status_t (*auth_compute_func)
+     (void *state, uint8_t *buffer, int octets_to_auth, 
+      int tag_len, uint8_t *tag);
+
+typedef err_status_t (*auth_update_func)
+     (void *state, uint8_t *buffer, int octets_to_auth);
+
+typedef err_status_t (*auth_start_func)(void *state);
+     
+/* some syntactic sugar on these function types */
+
+#define auth_type_alloc(at, a, klen, outlen)                        \
+                 ((at)->alloc((a), (klen), (outlen)))
+
+#define auth_init(a, key)                                           \
+                 (((a)->type)->init((a)->state, (key), ((a)->key_len)))
+
+#define auth_compute(a, buf, len, res)                              \
+       (((a)->type)->compute((a)->state, (buf), (len), (a)->out_len, (res)))
+
+#define auth_update(a, buf, len)                                    \
+       (((a)->type)->update((a)->state, (buf), (len)))
+
+#define auth_start(a)(((a)->type)->start((a)->state))
+
+#define auth_dealloc(c) (((c)->type)->dealloc(c))
+
+/* functions to get information about a particular auth_t */
+
+int
+auth_get_key_length(const struct auth_t *a);
+
+int
+auth_get_tag_length(const struct auth_t *a);
+
+int
+auth_get_prefix_length(const struct auth_t *a);
+
+/*
+ * auth_test_case_t is a (list of) key/message/tag values that are
+ * known to be correct for a particular cipher.  this data can be used
+ * to test an implementation in an on-the-fly self test of the
+ * correcness of the implementation.  (see the auth_type_self_test()
+ * function below)
+ */
+
+typedef struct auth_test_case_t {
+  int key_length_octets;                    /* octets in key            */
+  uint8_t *key;                             /* key                      */
+  int data_length_octets;                   /* octets in data           */ 
+  uint8_t *data;                            /* data                     */
+  int tag_length_octets;                    /* octets in tag            */
+  uint8_t *tag;                             /* tag                      */
+  struct auth_test_case_t *next_test_case;  /* pointer to next testcase */
+} auth_test_case_t;
+
+/* auth_type_t */
+
+typedef struct auth_type_t {
+  auth_alloc_func      alloc;
+  auth_dealloc_func    dealloc;
+  auth_init_func       init;
+  auth_compute_func    compute;
+  auth_update_func     update;
+  auth_start_func      start;
+  char                *description;
+  int                  ref_count;
+  auth_test_case_t    *test_data;
+  debug_module_t      *debug;
+} auth_type_t;
+
+typedef struct auth_t {
+  auth_type_t *type;
+  void        *state;                   
+  int          out_len;           /* length of output tag in octets */
+  int          key_len;           /* length of key in octets        */
+  int          prefix_len;        /* length of keystream prefix     */
+} auth_t;
+
+/* 
+ * auth_type_self_test() tests an auth_type against test cases
+ * provided in an array of values of key/message/tag that is known to
+ * be good
+ */
+
+err_status_t
+auth_type_self_test(const auth_type_t *at);
+
+/*
+ * auth_type_get_ref_count(at) returns the reference count (the number
+ * of instantiations) of the auth_type_t at
+ */
+
+int
+auth_type_get_ref_count(const auth_type_t *at);
+
+#endif /* AUTH_H */
diff --git a/jni/pjproject-android/.svn/pristine/21/21ffa0bd9cf5351f4650c5a1e9ac3190aade5592.svn-base b/jni/pjproject-android/.svn/pristine/21/21ffa0bd9cf5351f4650c5a1e9ac3190aade5592.svn-base
new file mode 100644
index 0000000..fb23b53
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/21/21ffa0bd9cf5351f4650c5a1e9ac3190aade5592.svn-base
@@ -0,0 +1,520 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_errno.h>
+#include <pjlib-util/errno.h>
+#include <pjlib-util/srv_resolver.h>
+#include <pj/addr_resolv.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE   "sip_resolve.c"
+
+#define ADDR_MAX_COUNT	    8
+
+struct naptr_target
+{
+    pj_str_t		    res_type;	    /**< e.g. "_sip._udp"   */
+    pj_str_t		    name;	    /**< Domain name.	    */
+    pjsip_transport_type_e  type;	    /**< Transport type.    */
+    unsigned		    order;	    /**< Order		    */
+    unsigned		    pref;	    /**< Preference.	    */
+};
+
+struct query
+{
+    char		    *objname;
+
+    pj_dns_type		     query_type;
+    void		    *token;
+    pjsip_resolver_callback *cb;
+    pj_dns_async_query	    *object;
+    pj_status_t		     last_error;
+
+    /* Original request: */
+    struct {
+	pjsip_host_info	     target;
+	unsigned	     def_port;
+    } req;
+
+    /* NAPTR records: */
+    unsigned		     naptr_cnt;
+    struct naptr_target	     naptr[8];
+};
+
+
+struct pjsip_resolver_t
+{
+    pj_dns_resolver *res;
+};
+
+
+static void srv_resolver_cb(void *user_data,
+			    pj_status_t status,
+			    const pj_dns_srv_record *rec);
+static void dns_a_callback(void *user_data,
+			   pj_status_t status,
+			   pj_dns_parsed_packet *response);
+
+
+/*
+ * Public API to create the resolver.
+ */
+PJ_DEF(pj_status_t) pjsip_resolver_create( pj_pool_t *pool,
+					   pjsip_resolver_t **p_res)
+{
+    pjsip_resolver_t *resolver;
+
+    PJ_ASSERT_RETURN(pool && p_res, PJ_EINVAL);
+    resolver = PJ_POOL_ZALLOC_T(pool, pjsip_resolver_t);
+    *p_res = resolver;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Public API to set the DNS resolver instance for the SIP resolver.
+ */
+PJ_DEF(pj_status_t) pjsip_resolver_set_resolver(pjsip_resolver_t *res,
+						pj_dns_resolver *dns_res)
+{
+#if PJSIP_HAS_RESOLVER
+    res->res = dns_res;
+    return PJ_SUCCESS;
+#else
+    PJ_UNUSED_ARG(res);
+    PJ_UNUSED_ARG(dns_res);
+    pj_assert(!"Resolver is disabled (PJSIP_HAS_RESOLVER==0)");
+    return PJ_EINVALIDOP;
+#endif
+}
+
+
+/*
+ * Public API to get the internal DNS resolver.
+ */
+PJ_DEF(pj_dns_resolver*) pjsip_resolver_get_resolver(pjsip_resolver_t *res)
+{
+    return res->res;
+}
+
+
+/*
+ * Public API to create destroy the resolver
+ */
+PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
+{
+    if (resolver->res) {
+#if PJSIP_HAS_RESOLVER
+	pj_dns_resolver_destroy(resolver->res, PJ_FALSE);
+#endif
+	resolver->res = NULL;
+    }
+}
+
+/*
+ * Internal:
+ *  determine if an address is a valid IP address, and if it is,
+ *  return the IP version (4 or 6).
+ */
+static int get_ip_addr_ver(const pj_str_t *host)
+{
+    pj_in_addr dummy;
+    pj_in6_addr dummy6;
+
+    /* First check with inet_aton() */
+    if (pj_inet_aton(host, &dummy) > 0)
+	return 4;
+
+    /* Then check if this is an IPv6 address */
+    if (pj_inet_pton(pj_AF_INET6(), host, &dummy6) == PJ_SUCCESS)
+	return 6;
+
+    /* Not an IP address */
+    return 0;
+}
+
+
+/*
+ * This is the main function for performing server resolution.
+ */
+PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
+			    pj_pool_t *pool,
+			    const pjsip_host_info *target,
+			    void *token,
+			    pjsip_resolver_callback *cb)
+{
+    pjsip_server_addresses svr_addr;
+    pj_status_t status = PJ_SUCCESS;
+    int ip_addr_ver;
+    struct query *query;
+    pjsip_transport_type_e type = target->type;
+
+    /* Is it IP address or hostname? And if it's an IP, which version? */
+    ip_addr_ver = get_ip_addr_ver(&target->addr.host);
+
+    /* Set the transport type if not explicitly specified. 
+     * RFC 3263 section 4.1 specify rules to set up this.
+     */
+    if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+	if (ip_addr_ver || (target->addr.port != 0)) {
+#if PJ_HAS_TCP
+	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
+	    {
+		type = PJSIP_TRANSPORT_TLS;
+	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
+	    {
+		type = PJSIP_TRANSPORT_TCP;
+	    } else 
+#endif
+	    {
+		type = PJSIP_TRANSPORT_UDP;
+	    }
+	} else {
+	    /* No type or explicit port is specified, and the address is
+	     * not IP address.
+	     * In this case, full NAPTR resolution must be performed.
+	     * But we don't support it (yet).
+	     */
+#if PJ_HAS_TCP
+	    if (target->flag & PJSIP_TRANSPORT_SECURE) 
+	    {
+		type = PJSIP_TRANSPORT_TLS;
+	    } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) 
+	    {
+		type = PJSIP_TRANSPORT_TCP;
+	    } else 
+#endif
+	    {
+		type = PJSIP_TRANSPORT_UDP;
+	    }
+	}
+
+	/* Add IPv6 flag for IPv6 address */
+	if (ip_addr_ver == 6)
+	    type = (pjsip_transport_type_e)((int)type + PJSIP_TRANSPORT_IPV6);
+    }
+
+
+    /* If target is an IP address, or if resolver is not configured, 
+     * we can just finish the resolution now using pj_gethostbyname()
+     */
+    if (ip_addr_ver || resolver->res == NULL) {
+	char addr_str[PJ_INET6_ADDRSTRLEN+10];
+	pj_uint16_t srv_port;
+
+	if (ip_addr_ver != 0) {
+	    /* Target is an IP address, no need to resolve */
+	    if (ip_addr_ver == 4) {
+		pj_sockaddr_init(pj_AF_INET(), &svr_addr.entry[0].addr, 
+				 NULL, 0);
+		pj_inet_aton(&target->addr.host,
+			     &svr_addr.entry[0].addr.ipv4.sin_addr);
+	    } else {
+		pj_sockaddr_init(pj_AF_INET6(), &svr_addr.entry[0].addr, 
+				 NULL, 0);
+		pj_inet_pton(pj_AF_INET6(), &target->addr.host,
+			     &svr_addr.entry[0].addr.ipv6.sin6_addr);
+	    }
+	} else {
+	    pj_addrinfo ai;
+	    unsigned count;
+	    int af;
+
+	    PJ_LOG(5,(THIS_FILE,
+		      "DNS resolver not available, target '%.*s:%d' type=%s "
+		      "will be resolved with getaddrinfo()",
+		      target->addr.host.slen,
+		      target->addr.host.ptr,
+		      target->addr.port,
+		      pjsip_transport_get_type_name(target->type)));
+
+	    if (type & PJSIP_TRANSPORT_IPV6) {
+		af = pj_AF_INET6();
+	    } else {
+		af = pj_AF_INET();
+	    }
+
+	    /* Resolve */
+	    count = 1;
+	    status = pj_getaddrinfo(af, &target->addr.host, &count, &ai);
+	    if (status != PJ_SUCCESS) {
+		/* "Normalize" error to PJ_ERESOLVE. This is a special error
+		 * because it will be translated to SIP status 502 by
+		 * sip_transaction.c
+		 */
+		status = PJ_ERESOLVE;
+		goto on_error;
+	    }
+
+	    svr_addr.entry[0].addr.addr.sa_family = (pj_uint16_t)af;
+	    pj_memcpy(&svr_addr.entry[0].addr, &ai.ai_addr,
+		      sizeof(pj_sockaddr));
+	}
+
+	/* Set the port number */
+	if (target->addr.port == 0) {
+	   srv_port = (pj_uint16_t)
+		      pjsip_transport_get_default_port_for_type(type);
+	} else {
+	   srv_port = (pj_uint16_t)target->addr.port;
+	}
+	pj_sockaddr_set_port(&svr_addr.entry[0].addr, srv_port);
+
+	/* Call the callback. */
+	PJ_LOG(5,(THIS_FILE, 
+		  "Target '%.*s:%d' type=%s resolved to "
+		  "'%s' type=%s (%s)",
+		  (int)target->addr.host.slen,
+		  target->addr.host.ptr,
+		  target->addr.port,
+		  pjsip_transport_get_type_name(target->type),
+		  pj_sockaddr_print(&svr_addr.entry[0].addr, addr_str,
+				    sizeof(addr_str), 3),
+		  pjsip_transport_get_type_name(type),
+		  pjsip_transport_get_type_desc(type)));
+	svr_addr.count = 1;
+	svr_addr.entry[0].priority = 0;
+	svr_addr.entry[0].weight = 0;
+	svr_addr.entry[0].type = type;
+	svr_addr.entry[0].addr_len = pj_sockaddr_get_len(&svr_addr.entry[0].addr);
+	(*cb)(status, token, &svr_addr);
+
+	/* Done. */
+	return;
+    }
+
+    /* Target is not an IP address so we need to resolve it. */
+#if PJSIP_HAS_RESOLVER
+
+    /* Build the query state */
+    query = PJ_POOL_ZALLOC_T(pool, struct query);
+    query->objname = THIS_FILE;
+    query->token = token;
+    query->cb = cb;
+    query->req.target = *target;
+    pj_strdup(pool, &query->req.target.addr.host, &target->addr.host);
+
+    /* If port is not specified, start with SRV resolution
+     * (should be with NAPTR, but we'll do that later)
+     */
+    PJ_TODO(SUPPORT_DNS_NAPTR);
+
+    /* Build dummy NAPTR entry */
+    query->naptr_cnt = 1;
+    pj_bzero(&query->naptr[0], sizeof(query->naptr[0]));
+    query->naptr[0].order = 0;
+    query->naptr[0].pref = 0;
+    query->naptr[0].type = type;
+    pj_strdup(pool, &query->naptr[0].name, &target->addr.host);
+
+
+    /* Start DNS SRV or A resolution, depending on whether port is specified */
+    if (target->addr.port == 0) {
+	query->query_type = PJ_DNS_TYPE_SRV;
+
+	query->req.def_port = 5060;
+
+	if (type == PJSIP_TRANSPORT_TLS) {
+	    query->naptr[0].res_type = pj_str("_sips._tcp.");
+	    query->req.def_port = 5061;
+	} else if (type == PJSIP_TRANSPORT_TCP)
+	    query->naptr[0].res_type = pj_str("_sip._tcp.");
+	else if (type == PJSIP_TRANSPORT_UDP)
+	    query->naptr[0].res_type = pj_str("_sip._udp.");
+	else {
+	    pj_assert(!"Unknown transport type");
+	    query->naptr[0].res_type = pj_str("_sip._udp.");
+	    
+	}
+
+    } else {
+	/* Otherwise if port is specified, start with A (or AAAA) host 
+	 * resolution 
+	 */
+	query->query_type = PJ_DNS_TYPE_A;
+	query->naptr[0].res_type.slen = 0;
+	query->req.def_port = target->addr.port;
+    }
+
+    /* Start the asynchronous query */
+    PJ_LOG(5, (query->objname, 
+	       "Starting async DNS %s query: target=%.*s%.*s, transport=%s, "
+	       "port=%d",
+	       pj_dns_get_type_name(query->query_type),
+	       (int)query->naptr[0].res_type.slen,
+	       query->naptr[0].res_type.ptr,
+	       (int)query->naptr[0].name.slen, query->naptr[0].name.ptr,
+	       pjsip_transport_get_type_name(target->type),
+	       target->addr.port));
+
+    if (query->query_type == PJ_DNS_TYPE_SRV) {
+
+	status = pj_dns_srv_resolve(&query->naptr[0].name,
+				    &query->naptr[0].res_type,
+				    query->req.def_port, pool, resolver->res,
+				    PJ_TRUE, query, &srv_resolver_cb, NULL);
+
+    } else if (query->query_type == PJ_DNS_TYPE_A) {
+
+	status = pj_dns_resolver_start_query(resolver->res, 
+					     &query->naptr[0].name,
+					     PJ_DNS_TYPE_A, 0, 
+					     &dns_a_callback,
+    					     query, &query->object);
+
+    } else {
+	pj_assert(!"Unexpected");
+	status = PJ_EBUG;
+    }
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    return;
+
+#else /* PJSIP_HAS_RESOLVER */
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(query);
+    PJ_UNUSED_ARG(srv_name);
+#endif /* PJSIP_HAS_RESOLVER */
+
+on_error:
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	PJ_LOG(4,(THIS_FILE, "Failed to resolve '%.*s'. Err=%d (%s)",
+			     (int)target->addr.host.slen,
+			     target->addr.host.ptr,
+			     status,
+			     pj_strerror(status,errmsg,sizeof(errmsg)).ptr));
+	(*cb)(status, token, NULL);
+	return;
+    }
+}
+
+#if PJSIP_HAS_RESOLVER
+
+/* 
+ * This callback is called when target is resolved with DNS A query.
+ */
+static void dns_a_callback(void *user_data,
+			   pj_status_t status,
+			   pj_dns_parsed_packet *pkt)
+{
+    struct query *query = (struct query*) user_data;
+    pjsip_server_addresses srv;
+    pj_dns_a_record rec;
+    unsigned i;
+
+    rec.addr_count = 0;
+
+    /* Parse the response */
+    if (status == PJ_SUCCESS) {
+	status = pj_dns_parse_a_response(pkt, &rec);
+    }
+
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	/* Log error */
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", 
+		  errmsg));
+
+	/* Call the callback */
+	(*query->cb)(status, query->token, NULL);
+	return;
+    }
+
+    /* Build server addresses and call callback */
+    srv.count = 0;
+    for (i=0; i<rec.addr_count; ++i) {
+	srv.entry[srv.count].type = query->naptr[0].type;
+	srv.entry[srv.count].priority = 0;
+	srv.entry[srv.count].weight = 0;
+	srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in);
+	pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4,
+			    0, (pj_uint16_t)query->req.def_port);
+	srv.entry[srv.count].addr.ipv4.sin_addr.s_addr =
+	    rec.addr[i].s_addr;
+
+	++srv.count;
+    }
+
+    /* Call the callback */
+    (*query->cb)(PJ_SUCCESS, query->token, &srv);
+}
+
+
+/* Callback to be called by DNS SRV resolution */
+static void srv_resolver_cb(void *user_data,
+			    pj_status_t status,
+			    const pj_dns_srv_record *rec)
+{
+    struct query *query = (struct query*) user_data;
+    pjsip_server_addresses srv;
+    unsigned i;
+
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+
+	/* Log error */
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(query->objname, "DNS A record resolution failed: %s", 
+		  errmsg));
+
+	/* Call the callback */
+	(*query->cb)(status, query->token, NULL);
+	return;
+    }
+
+    /* Build server addresses and call callback */
+    srv.count = 0;
+    for (i=0; i<rec->count; ++i) {
+	unsigned j;
+
+	for (j=0; j<rec->entry[i].server.addr_count; ++j) {
+	    srv.entry[srv.count].type = query->naptr[0].type;
+	    srv.entry[srv.count].priority = rec->entry[i].priority;
+	    srv.entry[srv.count].weight = rec->entry[i].weight;
+	    srv.entry[srv.count].addr_len = sizeof(pj_sockaddr_in);
+	    pj_sockaddr_in_init(&srv.entry[srv.count].addr.ipv4,
+				0, (pj_uint16_t)rec->entry[i].port);
+	    srv.entry[srv.count].addr.ipv4.sin_addr.s_addr =
+		rec->entry[i].server.addr[j].s_addr;
+
+	    ++srv.count;
+	}
+    }
+
+    /* Call the callback */
+    (*query->cb)(PJ_SUCCESS, query->token, &srv);
+}
+
+#endif	/* PJSIP_HAS_RESOLVER */
+