<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[Freeswitch-trunk][16779] </title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<div id="header">FreeSWITCH Subversion</div>
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://fisheye.freeswitch.org/changelog/FreeSWITCH?cs=16779">16779</a></dd>
<dt>Author</dt> <dd>sathieu</dd>
<dt>Date</dt> <dd>2010-02-24 06:04:08 -0600 (Wed, 24 Feb 2010)</dd>
</dl>

<h3>Log Message</h3>
<pre>Skinny: Milestone 3: Calls management

As of now, this is only proof of concept. How to test:
- only one phone registred
- call originate skinny/internal/7628 &amp;delay_echo(100) from console
- answer
- test the echo
- hangup</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchtrunksrcmodendpointsmod_skinnymod_skinnyc">freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchtrunksrcmodendpointsmod_skinnymod_skinnyc"></a>
<div class="modfile"><h4>Modified: freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c (16778 => 16779)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c        2010-02-24 12:03:59 UTC (rev 16778)
+++ freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c        2010-02-24 12:04:08 UTC (rev 16779)
</span><span class="lines">@@ -140,7 +140,10 @@
</span><span class="cx">         TFLAG_HANGUP = (1 &lt;&lt; 5),
</span><span class="cx">         TFLAG_LINEAR = (1 &lt;&lt; 6),
</span><span class="cx">         TFLAG_CODEC = (1 &lt;&lt; 7),
</span><del>-        TFLAG_BREAK = (1 &lt;&lt; 8)
</del><ins>+        TFLAG_BREAK = (1 &lt;&lt; 8),
+        
+        TFLAG_READING = (1 &lt;&lt; 9),
+        TFLAG_WRITING = (1 &lt;&lt; 10)
</ins><span class="cx"> } TFLAGS;
</span><span class="cx"> 
</span><span class="cx"> typedef enum {
</span><span class="lines">@@ -149,18 +152,34 @@
</span><span class="cx"> 
</span><span class="cx"> struct private_object {
</span><span class="cx">         unsigned int flags;
</span><del>-        switch_codec_t read_codec;
-        switch_codec_t write_codec;
</del><span class="cx">         switch_frame_t read_frame;
</span><span class="cx">         unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
</span><span class="cx">         switch_core_session_t *session;
</span><span class="cx">         switch_caller_profile_t *caller_profile;
</span><span class="cx">         switch_mutex_t *mutex;
</span><span class="cx">         switch_mutex_t *flag_mutex;
</span><del>-        char *dest;
</del><span class="cx">         /* identification */
</span><del>-        skinny_profile_t *profile;
</del><ins>+        struct listener *listener;
+        uint32_t line;
</ins><span class="cx">         uint32_t call_id;
</span><ins>+        uint32_t party_id;
+        /* codec */
+        char *iananame;        
+        switch_codec_t read_codec;
+        switch_codec_t write_codec;
+        switch_codec_implementation_t read_impl;
+        switch_codec_implementation_t write_impl;
+        unsigned long rm_rate;
+        uint32_t codec_ms;
+        char *rm_encoding;
+        char *rm_fmtp;
+        switch_payload_t agreed_pt;
+        /* RTP */
+        switch_rtp_t *rtp_session;
+        char *local_sdp_audio_ip;
+        switch_port_t local_sdp_audio_port;
+        char *remote_sdp_audio_ip;
+        switch_port_t remote_sdp_audio_port;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> typedef struct private_object private_t;
</span><span class="lines">@@ -801,6 +820,7 @@
</span><span class="cx"> struct listener {
</span><span class="cx">         skinny_profile_t *profile;
</span><span class="cx">         char device_name[16];
</span><ins>+        switch_core_session_t *outgoing_session;
</ins><span class="cx"> 
</span><span class="cx">         switch_socket_t *sock;
</span><span class="cx">         switch_memory_pool_t *pool;
</span><span class="lines">@@ -1060,12 +1080,134 @@
</span><span class="cx"> /* CHANNEL FUNCTIONS */
</span><span class="cx"> /*****************************************************************************/
</span><span class="cx"> 
</span><ins>+static switch_status_t skinny_tech_set_codec(private_t *tech_pvt, int force)
+{
+        int ms;
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+        int resetting = 0;
+
+        if (!tech_pvt-&gt;iananame) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_ERROR, &quot;No audio codec available\n&quot;);
+                switch_goto_status(SWITCH_STATUS_FALSE, end);
+        }
+
+        if (switch_core_codec_ready(&amp;tech_pvt-&gt;read_codec)) {
+                if (!force) {
+                        switch_goto_status(SWITCH_STATUS_SUCCESS, end);
+                }
+                if (strcasecmp(tech_pvt-&gt;read_impl.iananame, tech_pvt-&gt;iananame) ||
+                        tech_pvt-&gt;read_impl.samples_per_second != tech_pvt-&gt;rm_rate ||
+                        tech_pvt-&gt;codec_ms != (uint32_t)tech_pvt-&gt;read_impl.microseconds_per_packet / 1000) {
+                        
+                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_DEBUG, &quot;Changing Codec from %s@%dms to %s@%dms\n&quot;,
+                                                          tech_pvt-&gt;read_impl.iananame, tech_pvt-&gt;read_impl.microseconds_per_packet / 1000, 
+                                                          tech_pvt-&gt;rm_encoding, tech_pvt-&gt;codec_ms);
+                        
+                        switch_core_session_lock_codec_write(tech_pvt-&gt;session);
+                        switch_core_session_lock_codec_read(tech_pvt-&gt;session);
+                        resetting = 1;
+                        switch_core_codec_destroy(&amp;tech_pvt-&gt;read_codec);
+                        switch_core_codec_destroy(&amp;tech_pvt-&gt;write_codec);
+                } else {
+                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_DEBUG, &quot;Already using %s\n&quot;, tech_pvt-&gt;read_impl.iananame);
+                        switch_goto_status(SWITCH_STATUS_SUCCESS, end);
+                }
+        }
+        
+        if (switch_core_codec_init(&amp;tech_pvt-&gt;read_codec,
+                                                           tech_pvt-&gt;iananame,
+                                                           tech_pvt-&gt;rm_fmtp,
+                                                           tech_pvt-&gt;rm_rate,
+                                                           tech_pvt-&gt;codec_ms,
+                                                           1,
+                                                           SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt-&gt;profile-&gt;codec_flags */,
+                                                           NULL, switch_core_session_get_pool(tech_pvt-&gt;session)) != SWITCH_STATUS_SUCCESS) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_ERROR, &quot;Can't load codec?\n&quot;);
+                switch_goto_status(SWITCH_STATUS_FALSE, end);
+        }
+
+        if (switch_core_codec_init(&amp;tech_pvt-&gt;write_codec,
+                                                           tech_pvt-&gt;iananame,
+                                                           tech_pvt-&gt;rm_fmtp,
+                                                           tech_pvt-&gt;rm_rate,
+                                                           tech_pvt-&gt;codec_ms,
+                                                           1,
+                                                           SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | 0 /* TODO tech_pvt-&gt;profile-&gt;codec_flags */,
+                                                           NULL, switch_core_session_get_pool(tech_pvt-&gt;session)) != SWITCH_STATUS_SUCCESS) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_ERROR, &quot;Can't load codec?\n&quot;);
+                switch_goto_status(SWITCH_STATUS_FALSE, end);
+        }
+
+        switch_assert(tech_pvt-&gt;read_codec.implementation);
+        switch_assert(tech_pvt-&gt;write_codec.implementation);
+
+        tech_pvt-&gt;read_impl = *tech_pvt-&gt;read_codec.implementation;
+        tech_pvt-&gt;write_impl = *tech_pvt-&gt;write_codec.implementation;
+
+        switch_core_session_set_read_impl(tech_pvt-&gt;session, tech_pvt-&gt;read_codec.implementation);
+        switch_core_session_set_write_impl(tech_pvt-&gt;session, tech_pvt-&gt;write_codec.implementation);
+
+        if (switch_rtp_ready(tech_pvt-&gt;rtp_session)) {
+                switch_assert(tech_pvt-&gt;read_codec.implementation);
+
+                if (switch_rtp_change_interval(tech_pvt-&gt;rtp_session, 
+                                                                           tech_pvt-&gt;read_impl.microseconds_per_packet,
+                                                                           tech_pvt-&gt;read_impl.samples_per_packet
+                                                                           ) != SWITCH_STATUS_SUCCESS) {
+                        /* TODO
+                        switch_channel_hangup(tech_pvt-&gt;channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+                        */
+                        switch_goto_status(SWITCH_STATUS_FALSE, end);                                
+                }
+        }
+
+        tech_pvt-&gt;read_frame.rate = tech_pvt-&gt;rm_rate;
+        ms = tech_pvt-&gt;write_codec.implementation-&gt;microseconds_per_packet / 1000;
+
+        if (!switch_core_codec_ready(&amp;tech_pvt-&gt;read_codec)) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_ERROR, &quot;Can't load codec?\n&quot;);
+                switch_goto_status(SWITCH_STATUS_FALSE, end);
+        }
+
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_DEBUG, &quot;Set Codec %s %s/%ld %d ms %d samples\n&quot;,
+                                          &quot;&quot; /* TODO switch_channel_get_name(tech_pvt-&gt;channel)*/, tech_pvt-&gt;iananame, tech_pvt-&gt;rm_rate, tech_pvt-&gt;codec_ms,
+                                          tech_pvt-&gt;read_impl.samples_per_packet);
+        tech_pvt-&gt;read_frame.codec = &amp;tech_pvt-&gt;read_codec;
+
+        tech_pvt-&gt;write_codec.agreed_pt = tech_pvt-&gt;agreed_pt;
+        tech_pvt-&gt;read_codec.agreed_pt = tech_pvt-&gt;agreed_pt;
+
+        if (force != 2) {
+                switch_core_session_set_read_codec(tech_pvt-&gt;session, &amp;tech_pvt-&gt;read_codec);
+                switch_core_session_set_write_codec(tech_pvt-&gt;session, &amp;tech_pvt-&gt;write_codec);
+        }
+
+        /* TODO
+        tech_pvt-&gt;fmtp_out = switch_core_session_strdup(tech_pvt-&gt;session, tech_pvt-&gt;write_codec.fmtp_out);
+        */
+
+        /* TODO
+        if (switch_rtp_ready(tech_pvt-&gt;rtp_session)) {
+                switch_rtp_set_default_payload(tech_pvt-&gt;rtp_session, tech_pvt-&gt;pt);
+        }
+        */
+
+ end:
+        if (resetting) {
+                switch_core_session_unlock_codec_write(tech_pvt-&gt;session);
+                switch_core_session_unlock_codec_read(tech_pvt-&gt;session);
+        }
+
+        return status;
+}
+
</ins><span class="cx"> static void tech_init(private_t *tech_pvt, switch_core_session_t *session)
</span><span class="cx"> {
</span><span class="cx">         tech_pvt-&gt;read_frame.data = tech_pvt-&gt;databuf;
</span><span class="cx">         tech_pvt-&gt;read_frame.buflen = sizeof(tech_pvt-&gt;databuf);
</span><span class="cx">         switch_mutex_init(&amp;tech_pvt-&gt;mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
</span><span class="cx">         switch_mutex_init(&amp;tech_pvt-&gt;flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session));
</span><ins>+        tech_pvt-&gt;call_id = 12345; /* TODO */
</ins><span class="cx">         switch_core_session_set_private(session, tech_pvt);
</span><span class="cx">         tech_pvt-&gt;session = session;
</span><span class="cx"> }
</span><span class="lines">@@ -1165,6 +1307,7 @@
</span><span class="cx"> {
</span><span class="cx">         switch_channel_t *channel = NULL;
</span><span class="cx">         private_t *tech_pvt = NULL;
</span><ins>+        listener_t *listener = NULL;
</ins><span class="cx"> 
</span><span class="cx">         channel = switch_core_session_get_channel(session);
</span><span class="cx">         assert(channel != NULL);
</span><span class="lines">@@ -1172,12 +1315,38 @@
</span><span class="cx">         tech_pvt = switch_core_session_get_private(session);
</span><span class="cx">         assert(tech_pvt != NULL);
</span><span class="cx"> 
</span><ins>+        listener = tech_pvt-&gt;listener;
+        assert(listener != NULL);
+        
</ins><span class="cx">         switch_clear_flag_locked(tech_pvt, TFLAG_IO);
</span><span class="cx">         switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
</span><del>-        //switch_thread_cond_signal(tech_pvt-&gt;cond);
</del><span class="cx"> 
</span><ins>+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, &quot;%s CHANNEL HANGUP\n&quot;, switch_channel_get_name(channel));
</ins><span class="cx"> 
</span><del>-        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, &quot;%s CHANNEL HANGUP\n&quot;, switch_channel_get_name(channel));
</del><ins>+        stop_tone(listener, tech_pvt-&gt;line, tech_pvt-&gt;call_id);
+        set_lamp(listener, SKINNY_BUTTON_LINE, tech_pvt-&gt;line, SKINNY_LAMP_OFF);
+        clear_prompt_status(listener, tech_pvt-&gt;line, tech_pvt-&gt;call_id);
+        close_receive_channel(listener,
+                tech_pvt-&gt;call_id, /* uint32_t conference_id, */
+                tech_pvt-&gt;party_id, /* uint32_t pass_thru_party_id, */
+                tech_pvt-&gt;call_id /* uint32_t conference_id2, */
+        );
+        stop_media_transmission(listener,
+                tech_pvt-&gt;call_id, /* uint32_t conference_id, */
+                tech_pvt-&gt;party_id, /* uint32_t pass_thru_party_id, */
+                tech_pvt-&gt;call_id /* uint32_t conference_id2, */
+        );
+        send_call_state(listener,
+                SKINNY_ON_HOOK,
+                tech_pvt-&gt;line,
+                tech_pvt-&gt;call_id);
+        send_select_soft_keys(listener, tech_pvt-&gt;line, tech_pvt-&gt;call_id,
+                SKINNY_KEY_SET_ON_HOOK, 0xffff);
+        /* TODO: DefineTimeDate */
+        set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
+        set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0);
+        
+
</ins><span class="cx">         switch_mutex_lock(globals.calls_mutex);
</span><span class="cx">         globals.calls--;
</span><span class="cx">         if (globals.calls &lt; 0) {
</span><span class="lines">@@ -1212,6 +1381,8 @@
</span><span class="cx">         default:
</span><span class="cx">                 break;
</span><span class="cx">         }
</span><ins>+        
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, &quot;%s CHANNEL KILL %d\n&quot;, switch_channel_get_name(channel), sig);
</ins><span class="cx"> 
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><span class="lines">@@ -1240,58 +1411,74 @@
</span><span class="cx"> {
</span><span class="cx">         switch_channel_t *channel = NULL;
</span><span class="cx">         private_t *tech_pvt = NULL;
</span><del>-        //switch_time_t started = switch_time_now();
-        //unsigned int elapsed;
-        switch_byte_t *data;
</del><ins>+        int payload = 0;
</ins><span class="cx"> 
</span><span class="cx">         channel = switch_core_session_get_channel(session);
</span><span class="cx">         assert(channel != NULL);
</span><span class="cx"> 
</span><span class="cx">         tech_pvt = switch_core_session_get_private(session);
</span><span class="cx">         assert(tech_pvt != NULL);
</span><del>-        tech_pvt-&gt;read_frame.flags = SFF_NONE;
-        *frame = NULL;
</del><span class="cx"> 
</span><del>-        while (switch_test_flag(tech_pvt, TFLAG_IO)) {
-
-                if (switch_test_flag(tech_pvt, TFLAG_BREAK)) {
-                        switch_clear_flag(tech_pvt, TFLAG_BREAK);
-                        goto cng;
</del><ins>+        while (!(tech_pvt-&gt;read_codec.implementation &amp;&amp; switch_rtp_ready(tech_pvt-&gt;rtp_session))) {
+                if (switch_channel_ready(channel)) {
+                        switch_yield(10000);
+                } else {
+                        return SWITCH_STATUS_GENERR;
</ins><span class="cx">                 }
</span><ins>+        }
</ins><span class="cx"> 
</span><del>-                if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
-                        return SWITCH_STATUS_FALSE;
-                }
</del><ins>+        tech_pvt-&gt;read_frame.datalen = 0;
+        switch_set_flag_locked(tech_pvt, TFLAG_READING);
</ins><span class="cx"> 
</span><del>-                if (switch_test_flag(tech_pvt, TFLAG_IO) &amp;&amp; switch_test_flag(tech_pvt, TFLAG_VOICE)) {
-                        switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
-                        if (!tech_pvt-&gt;read_frame.datalen) {
-                                continue;
</del><ins>+        if (switch_test_flag(tech_pvt, TFLAG_IO)) {
+                switch_status_t status;
+
+                switch_assert(tech_pvt-&gt;rtp_session != NULL);
+                tech_pvt-&gt;read_frame.datalen = 0;
+
+
+                while (switch_test_flag(tech_pvt, TFLAG_IO) &amp;&amp; tech_pvt-&gt;read_frame.datalen == 0) {
+                        tech_pvt-&gt;read_frame.flags = SFF_NONE;
+
+                        status = switch_rtp_zerocopy_read_frame(tech_pvt-&gt;rtp_session, &amp;tech_pvt-&gt;read_frame, flags);
+                        if (status != SWITCH_STATUS_SUCCESS &amp;&amp; status != SWITCH_STATUS_BREAK) {
+                                return SWITCH_STATUS_FALSE;
</ins><span class="cx">                         }
</span><del>-                        *frame = &amp;tech_pvt-&gt;read_frame;
-#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
-                        if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) {
-                                switch_swap_linear((*frame)-&gt;data, (int) (*frame)-&gt;datalen / 2);
</del><ins>+
+                        payload = tech_pvt-&gt;read_frame.payload;
+
+                        if (switch_rtp_has_dtmf(tech_pvt-&gt;rtp_session)) {
+                                switch_dtmf_t dtmf = { 0 };
+                                switch_rtp_dequeue_dtmf(tech_pvt-&gt;rtp_session, &amp;dtmf);
+                                switch_channel_queue_dtmf(channel, &amp;dtmf);
</ins><span class="cx">                         }
</span><del>-#endif
-                        return SWITCH_STATUS_SUCCESS;
</del><ins>+
+
+                        if (tech_pvt-&gt;read_frame.datalen &gt; 0) {
+                                size_t bytes = 0;
+                                int frames = 1;
+
+                                if (!switch_test_flag((&amp;tech_pvt-&gt;read_frame), SFF_CNG)) {
+                                        if ((bytes = tech_pvt-&gt;read_codec.implementation-&gt;encoded_bytes_per_packet)) {
+                                                frames = (tech_pvt-&gt;read_frame.datalen / bytes);
+                                        }
+                                        tech_pvt-&gt;read_frame.samples = (int) (frames * tech_pvt-&gt;read_codec.implementation-&gt;samples_per_packet);
+                                }
+                                break;
+                        }
</ins><span class="cx">                 }
</span><del>-
-                switch_cond_next();
</del><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        switch_clear_flag_locked(tech_pvt, TFLAG_READING);
</ins><span class="cx"> 
</span><del>-        return SWITCH_STATUS_FALSE;
</del><ins>+        if (tech_pvt-&gt;read_frame.datalen == 0) {
+                *frame = NULL;
+                return SWITCH_STATUS_GENERR;
+        }
</ins><span class="cx"> 
</span><del>-  cng:
-        data = (switch_byte_t *) tech_pvt-&gt;read_frame.data;
-        data[0] = 65;
-        data[1] = 0;
-        tech_pvt-&gt;read_frame.datalen = 2;
-        tech_pvt-&gt;read_frame.flags = SFF_CNG;
</del><span class="cx">         *frame = &amp;tech_pvt-&gt;read_frame;
</span><ins>+
</ins><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><del>-
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
</span><span class="lines">@@ -1299,7 +1486,8 @@
</span><span class="cx">         switch_channel_t *channel = NULL;
</span><span class="cx">         private_t *tech_pvt = NULL;
</span><span class="cx">         //switch_frame_t *pframe;
</span><del>-
</del><ins>+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+        
</ins><span class="cx">         channel = switch_core_session_get_channel(session);
</span><span class="cx">         assert(channel != NULL);
</span><span class="cx"> 
</span><span class="lines">@@ -1315,9 +1503,14 @@
</span><span class="cx">         }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+        switch_set_flag_locked(tech_pvt, TFLAG_WRITING);
</ins><span class="cx"> 
</span><del>-        return SWITCH_STATUS_SUCCESS;
</del><ins>+        switch_rtp_write_frame(tech_pvt-&gt;rtp_session, frame);
</ins><span class="cx"> 
</span><ins>+        switch_clear_flag_locked(tech_pvt, TFLAG_WRITING);
+
+        return status;
+
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static switch_status_t channel_answer_channel(switch_core_session_t *session)
</span><span class="lines">@@ -1413,8 +1606,6 @@
</span><span class="cx">                 goto error;
</span><span class="cx">         }
</span><span class="cx">         
</span><del>-        tech_pvt-&gt;profile = profile;
-        tech_pvt-&gt;dest = switch_core_session_strdup(nsession, dest);
</del><span class="cx">         snprintf(name, sizeof(name), &quot;SKINNY/%s/%s&quot;, profile-&gt;name, dest);
</span><span class="cx"> 
</span><span class="cx">         channel = switch_core_session_get_channel(nsession);
</span><span class="lines">@@ -1427,15 +1618,58 @@
</span><span class="cx"> 
</span><span class="cx">         switch_channel_set_flag(channel, CF_OUTBOUND);
</span><span class="cx">         switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND);
</span><del>-        switch_channel_set_state(channel, CS_INIT);
</del><span class="cx"> 
</span><del>-        cause = SWITCH_CAUSE_CHAN_NOT_IMPLEMENTED;
-        
-        if (!(cause == SWITCH_CAUSE_SUCCESS)) {
</del><ins>+        /* TODO: find listener(s) based on profile and dest */
+        tech_pvt-&gt;listener = profile-&gt;listeners;
+
+        if (!tech_pvt-&gt;listener) {
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, &quot;Invalid destination %s in profile %s\n&quot;, dest, profile_name);
+                cause = SWITCH_CAUSE_UNALLOCATED_NUMBER;
</ins><span class="cx">                 goto error;
</span><span class="cx">         }
</span><ins>+
+        /* TODO find line */
+        tech_pvt-&gt;line = 1;
</ins><span class="cx">         
</span><ins>+        tech_pvt-&gt;listener-&gt;outgoing_session = nsession;
+        send_call_state(tech_pvt-&gt;listener, SKINNY_RING_IN, tech_pvt-&gt;line, tech_pvt-&gt;call_id);
+        send_select_soft_keys(tech_pvt-&gt;listener, tech_pvt-&gt;line, tech_pvt-&gt;call_id,
+                SKINNY_KEY_SET_RING_IN, 0xffff);
+        display_prompt_status(tech_pvt-&gt;listener, 0, &quot;\200\027tel&quot;, tech_pvt-&gt;line, tech_pvt-&gt;call_id);
+        /* displayprinotifiymessage */
+        send_call_info(tech_pvt-&gt;listener,
+                &quot;TODO&quot;, /* char calling_party_name[40], */
+                &quot;TODO&quot;, /* char calling_party[24], */
+                &quot;TODO&quot;, /* char called_party_name[40], */
+                &quot;TODO&quot;, /* char called_party[24], */
+                tech_pvt-&gt;line, /* uint32_t line_instance, */
+                tech_pvt-&gt;call_id, /* uint32_t call_id, */
+                SKINNY_OUTBOUND_CALL, /* uint32_t call_type, */
+                &quot;TODO&quot;, /* char original_called_party_name[40], */
+                &quot;TODO&quot;, /* char original_called_party[24], */
+                &quot;TODO&quot;, /* char last_redirecting_party_name[40], */
+                &quot;TODO&quot;, /* char last_redirecting_party[24], */
+                0, /* uint32_t original_called_party_redirect_reason, */
+                0, /* uint32_t last_redirecting_reason, */
+                &quot;TODO&quot;, /* char calling_party_voice_mailbox[24], */
+                &quot;TODO&quot;, /* char called_party_voice_mailbox[24], */
+                &quot;TODO&quot;, /* char original_called_party_voice_mailbox[24], */
+                &quot;TODO&quot;, /* char last_redirecting_voice_mailbox[24], */
+                1, /* uint32_t call_instance, */
+                1, /* uint32_t call_security_status, */
+                0 /* uint32_t party_pi_restriction_bits */
+        );
+        set_lamp(tech_pvt-&gt;listener, SKINNY_BUTTON_LINE, tech_pvt-&gt;line, SKINNY_LAMP_BLINK);
+        set_ringer(tech_pvt-&gt;listener, SKINNY_RING_OUTSIDE, SKINNY_RING_FOREVER, 0);
+
</ins><span class="cx">         *new_session = nsession;
</span><ins>+
+        /* ?? switch_channel_mark_ring_ready(channel); */
+
+        if (switch_channel_get_state(channel) == CS_NEW) {
+                switch_channel_set_state(channel, CS_INIT);
+        }
+
</ins><span class="cx">         cause = SWITCH_CAUSE_SUCCESS;
</span><span class="cx">         goto done;
</span><span class="cx"> 
</span><span class="lines">@@ -2561,6 +2795,165 @@
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static switch_status_t skinny_handle_off_hook_message(listener_t *listener, skinny_message_t *request)
+{
+        skinny_profile_t *profile;
+
+        switch_assert(listener-&gt;profile);
+        switch_assert(listener-&gt;device_name);
+
+        profile = listener-&gt;profile;
+
+        skinny_check_data_length(request, sizeof(request-&gt;data.off_hook));
+
+        if(listener-&gt;outgoing_session) { /*answering a call */
+                private_t *tech_pvt = NULL;
+                tech_pvt = switch_core_session_get_private(listener-&gt;outgoing_session);
+                if(request-&gt;data.off_hook.line_instance) {
+                        tech_pvt-&gt;line = request-&gt;data.off_hook.line_instance;
+                } else {
+                        tech_pvt-&gt;line = 1;
+                }
+                set_ringer(listener, SKINNY_RING_OFF, SKINNY_RING_FOREVER, 0); /* TODO : here ? */
+                stop_tone(listener, tech_pvt-&gt;line, tech_pvt-&gt;call_id);
+                open_receive_channel(listener,
+                        tech_pvt-&gt;call_id, /* uint32_t conference_id, */
+                        0, /* uint32_t pass_thru_party_id, */
+                        20, /* uint32_t packets, */
+                        SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */
+                        0, /* uint32_t echo_cancel_type, */
+                        0, /* uint32_t g723_bitrate, */
+                        0, /* uint32_t conference_id2, */
+                        0 /* uint32_t reserved[10] */
+                );
+                send_call_state(listener,
+                        SKINNY_CONNECTED,
+                        tech_pvt-&gt;line,
+                        tech_pvt-&gt;call_id);
+                send_select_soft_keys(listener,
+                        tech_pvt-&gt;line,
+                        tech_pvt-&gt;call_id,
+                        SKINNY_KEY_SET_CONNECTED,
+                        0xffff);
+                display_prompt_status(listener,
+                        0,
+                        &quot;\200\030&quot;,
+                        1,
+                        tech_pvt-&gt;call_id);
+        }
+        return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t skinny_handle_open_receive_channel_ack_message(listener_t *listener, skinny_message_t *request)
+{
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+        skinny_profile_t *profile;
+
+        switch_assert(listener-&gt;profile);
+        switch_assert(listener-&gt;device_name);
+
+        profile = listener-&gt;profile;
+
+        skinny_check_data_length(request, sizeof(request-&gt;data.open_receive_channel_ack));
+
+        if(listener-&gt;outgoing_session) {
+                const char *err = NULL;
+                private_t *tech_pvt = NULL;
+                switch_channel_t *channel = NULL;
+                struct in_addr addr;
+
+                tech_pvt = switch_core_session_get_private(listener-&gt;outgoing_session);
+                channel = switch_core_session_get_channel(listener-&gt;outgoing_session);
+
+                /* Codec */
+                tech_pvt-&gt;iananame = &quot;PCMU&quot;; /* TODO */
+                tech_pvt-&gt;codec_ms = 10; /* TODO */
+                tech_pvt-&gt;rm_rate = 8000; /* TODO */
+                tech_pvt-&gt;rm_fmtp = NULL; /* TODO */
+                tech_pvt-&gt;agreed_pt = (switch_payload_t) 0; /* TODO */
+                tech_pvt-&gt;rm_encoding = switch_core_strdup(switch_core_session_get_pool(listener-&gt;outgoing_session), &quot;&quot;);
+                skinny_tech_set_codec(tech_pvt, 0);
+                if ((status = skinny_tech_set_codec(tech_pvt, 0)) != SWITCH_STATUS_SUCCESS) {
+                        goto end;
+                }
+                
+                /* Request a local port from the core's allocator */
+                if (!(tech_pvt-&gt;local_sdp_audio_port = switch_rtp_request_port(listener-&gt;profile-&gt;ip))) {
+                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_CRIT, &quot;No RTP ports available!\n&quot;);
+                        return SWITCH_STATUS_FALSE;
+                }
+                tech_pvt-&gt;local_sdp_audio_ip = switch_core_strdup(switch_core_session_get_pool(listener-&gt;outgoing_session), listener-&gt;profile-&gt;ip);
+
+                tech_pvt-&gt;remote_sdp_audio_ip = inet_ntoa(request-&gt;data.open_receive_channel_ack.ip);
+                tech_pvt-&gt;remote_sdp_audio_port = request-&gt;data.open_receive_channel_ack.port;
+
+                tech_pvt-&gt;rtp_session = switch_rtp_new(tech_pvt-&gt;local_sdp_audio_ip,
+                                                                                           tech_pvt-&gt;local_sdp_audio_port,
+                                                                                           tech_pvt-&gt;remote_sdp_audio_ip,
+                                                                                           tech_pvt-&gt;remote_sdp_audio_port,
+                                                                                           tech_pvt-&gt;agreed_pt,
+                                                                                           tech_pvt-&gt;read_impl.samples_per_packet,
+                                                                                           tech_pvt-&gt;codec_ms * 1000,
+                                                                                           (switch_rtp_flag_t) 0, &quot;soft&quot;, &amp;err,
+                                                                                           switch_core_session_get_pool(listener-&gt;outgoing_session));
+                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt-&gt;session), SWITCH_LOG_DEBUG,
+                                                  &quot;AUDIO RTP [%s] %s:%d-&gt;%s:%d codec: %u ms: %d [%s]\n&quot;,
+                                                  switch_channel_get_name(channel),
+                                                  tech_pvt-&gt;local_sdp_audio_ip,
+                                                  tech_pvt-&gt;local_sdp_audio_port,
+                                                  tech_pvt-&gt;remote_sdp_audio_ip,
+                                                  tech_pvt-&gt;remote_sdp_audio_port,
+                                                  tech_pvt-&gt;agreed_pt,
+                                                  tech_pvt-&gt;read_impl.microseconds_per_packet / 1000,
+                                                  switch_rtp_ready(tech_pvt-&gt;rtp_session) ? &quot;SUCCESS&quot; : err);
+                inet_aton(tech_pvt-&gt;local_sdp_audio_ip, &amp;addr);
+                start_media_transmission(listener,
+                        tech_pvt-&gt;call_id, /* uint32_t conference_id, */
+                        tech_pvt-&gt;party_id, /* uint32_t pass_thru_party_id, */
+                        addr.s_addr, /* uint32_t remote_ip, */
+                        tech_pvt-&gt;local_sdp_audio_port, /* uint32_t remote_port, */
+                        20, /* uint32_t ms_per_packet, */
+                        SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */
+                        184, /* uint32_t precedence, */
+                        0, /* uint32_t silence_suppression, */
+                        0, /* uint16_t max_frames_per_packet, */
+                        0 /* uint32_t g723_bitrate */
+                );
+                switch_channel_mark_answered(channel);
+        }
+end:
+        return status;
+}
+
+static switch_status_t skinny_handle_on_hook_message(listener_t *listener, skinny_message_t *request)
+{
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+        skinny_profile_t *profile;
+
+        switch_assert(listener-&gt;profile);
+        switch_assert(listener-&gt;device_name);
+
+        profile = listener-&gt;profile;
+
+        skinny_check_data_length(request, sizeof(request-&gt;data.on_hook));
+
+        if(listener-&gt;outgoing_session) {
+                switch_channel_t *channel = NULL;
+                private_t *tech_pvt = NULL;
+
+                channel = switch_core_session_get_channel(listener-&gt;outgoing_session);
+                assert(channel != NULL);
+
+                tech_pvt = switch_core_session_get_private(listener-&gt;outgoing_session);
+                assert(tech_pvt != NULL);
+
+                switch_clear_flag_locked(tech_pvt, TFLAG_IO);
+                switch_clear_flag_locked(tech_pvt, TFLAG_VOICE);
+                switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+        }
+        return status;
+}
+
</ins><span class="cx"> static switch_status_t skinny_handle_unregister(listener_t *listener, skinny_message_t *request)
</span><span class="cx"> {
</span><span class="cx">         switch_event_t *event = NULL;
</span><span class="lines">@@ -2610,83 +3003,20 @@
</span><span class="cx">                 /* live phase */
</span><span class="cx">                 case KEEP_ALIVE_MESSAGE:
</span><span class="cx">                         return skinny_handle_keep_alive_message(listener, request);
</span><ins>+                case OFF_HOOK_MESSAGE:
+                        return skinny_handle_off_hook_message(listener, request);
+                case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
+                        return skinny_handle_open_receive_channel_ack_message(listener, request);
+                case ON_HOOK_MESSAGE:
+                        return skinny_handle_on_hook_message(listener, request);
</ins><span class="cx">                 /* end phase */
</span><span class="cx">                 case UNREGISTER_MESSAGE:
</span><span class="cx">                         return skinny_handle_unregister(listener, request);
</span><span class="cx">                 case 0xABCDEF: /* the following commands are to avoid compile warnings (which are errors) */
</span><span class="cx"> activate_call_plane(listener, 1 /* line */);
</span><del>-send_select_soft_keys(listener, 1 /* line */, 0 /* call_id */, SKINNY_KEY_SET_RING_OUT, 0xffff);
</del><span class="cx"> send_dialed_number(listener, 0 /* called_party */, 1 /* line */, 0 /* call_id */);
</span><del>-send_call_state(listener, SKINNY_PROCEED, 1 /* line */, 0 /* call_id */);
-open_receive_channel(listener,
-        0, /* uint32_t conference_id, */
-        0, /* uint32_t pass_thru_party_id, */
-        20, /* uint32_t packets, */
-        SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */
-        0, /* uint32_t echo_cancel_type, */
-        0, /* uint32_t g723_bitrate, */
-        0, /* uint32_t conference_id2, */
-        0 /* uint32_t reserved[10] */
-);
-start_media_transmission(listener,
-        0, /* uint32_t conference_id, */
-        0, /* uint32_t pass_thru_party_id, */
-        0, /* uint32_t remote_ip, */
-        0, /* uint32_t remote_port, */
-        20, /* uint32_t ms_per_packet, */
-        SKINNY_CODEC_ULAW_64K, /* uint32_t payload_capacity, */
-        184, /* uint32_t precedence, */
-        0, /* uint32_t silence_suppression, */
-        0, /* uint16_t max_frames_per_packet, */
-        0 /* uint32_t g723_bitrate */
-);
-close_receive_channel(listener,
-        0, /* uint32_t conference_id, */
-        0, /* uint32_t pass_thru_party_id, */
-        0 /* uint32_t conference_id2, */
-);
-stop_media_transmission(listener,
-        0, /* uint32_t conference_id, */
-        0, /* uint32_t pass_thru_party_id, */
-        0 /* uint32_t conference_id2, */
-);
</del><span class="cx"> start_tone(listener, SKINNY_TONE_DIALTONE, 0, 0, 0);
</span><del>-stop_tone(listener, 0, 0);
-clear_prompt_status(listener, 0, 0);
-set_speaker_mode(listener, SKINNY_SPEAKER_OFF);
</del><span class="cx"> 
</span><del>-send_call_state(listener, SKINNY_RING_IN, 0, 0);
-send_select_soft_keys(listener, 0, 0,
-        SKINNY_KEY_SET_RING_IN, 0xffff);
-display_prompt_status(listener, 0, &quot;\200\027tel&quot;, 0, 0);
-/* displayprinotifiymessage */
-send_call_info(listener,
-        &quot;TODO&quot;, /* char calling_party_name[40], */
-        &quot;TODO&quot;, /* char calling_party[24], */
-        &quot;TODO&quot;, /* char called_party_name[40], */
-        &quot;TODO&quot;, /* char called_party[24], */
-        0, /* uint32_t line_instance, */
-        0, /* uint32_t call_id, */
-        SKINNY_OUTBOUND_CALL, /* uint32_t call_type, */
-        &quot;TODO&quot;, /* char original_called_party_name[40], */
-        &quot;TODO&quot;, /* char original_called_party[24], */
-        &quot;TODO&quot;, /* char last_redirecting_party_name[40], */
-        &quot;TODO&quot;, /* char last_redirecting_party[24], */
-        0, /* uint32_t original_called_party_redirect_reason, */
-        0, /* uint32_t last_redirecting_reason, */
-        &quot;TODO&quot;, /* char calling_party_voice_mailbox[24], */
-        &quot;TODO&quot;, /* char called_party_voice_mailbox[24], */
-        &quot;TODO&quot;, /* char original_called_party_voice_mailbox[24], */
-        &quot;TODO&quot;, /* char last_redirecting_voice_mailbox[24], */
-        1, /* uint32_t call_instance, */
-        1, /* uint32_t call_security_status, */
-        0 /* uint32_t party_pi_restriction_bits */
-);
-set_lamp(listener, SKINNY_BUTTON_LINE, 0, SKINNY_LAMP_BLINK);
-set_ringer(listener, SKINNY_RING_OUTSIDE, SKINNY_RING_FOREVER, 0);
-
-
-
</del><span class="cx">                 default:
</span><span class="cx">                         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, 
</span><span class="cx">                                 &quot;Unknown request type: %x (length=%d).\n&quot;, request-&gt;type, request-&gt;length);
</span></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>

</body>
</html>