* #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 */
+