# 04_pjnath_ice_strans.patch   (rebased for pjsip 2.17 — full port)
#
# Add to pjnath's ICE stream transport:
#   * on_ice_state callback on pj_ice_strans_cb (fired on every state
#     transition: NULL -> INIT -> READY -> SESS_READY -> NEGO ->
#     RUNNING/FAILED).
#   * set_ice_state() helper that wraps the state-assignment + cb
#     invocation, used at every state assignment in the .c file.
#   * pj_ice_strans_get_session() accessor — returns ice_st->ice.
#   * pj_ice_strans_get_start_time() accessor — returns
#     ice_st->start_time (the timestamp pj_gettimeofday()'d when
#     ICE negotiation began).
#
# Patch 11_transport_ice.patch (pjmedia layer) wires the pjmedia
# transport_ice up to on_ice_state. Without this patch the build
# fails with:
#     error: no member named 'on_ice_state' in 'struct pj_ice_strans_cb'
#
# Rebase notes (vs the 2.12 version):
#   * 2.17 has on_new_candidate (trickle ICE) already in
#     pj_ice_strans_cb between on_ice_complete and what would be
#     on_ice_state. We add on_ice_state AFTER on_new_candidate.
#   * 2.12 moved the pj_ice_strans_state enum from after
#     pj_ice_strans_cfg to before pj_ice_strans_cb so the cb could
#     reference the enum by name. To avoid moving the enum (line
#     drift is enormous), we declare on_ice_state with `int` prev/
#     curr — the actual values are still pj_ice_strans_state values,
#     just untyped at the cb level. Callers (transport_ice.c) cast
#     to pj_ice_strans_state for the switch.
#   * pj_ice_strans_get_session()/get_start_time() inserted after
#     pj_ice_strans_change_role (around line 1624 in 2.17) rather
#     than 2.12's spot before pj_ice_strans_stop_ice — both are
#     valid file locations; this one has less line drift.
#
--- pjsip_orig/pjnath/include/pjnath/ice_strans.h
+++ pjsip/pjnath/include/pjnath/ice_strans.h
@@ -217,5 +217,17 @@
     void    (*on_new_candidate)(pj_ice_strans *ice_st,
                                 const pj_ice_sess_cand *cand,
                                 pj_bool_t end_of_cand);
+
+    /**
+     * Callback to report ICE state changes. prev/curr carry
+     * pj_ice_strans_state values; typed int because the enum is
+     * declared further down in this header (after
+     * pj_ice_strans_cfg) and we don't want to move it.
+     *
+     * @param ice_st       The ICE stream transport.
+     * @param prev         Previous state (pj_ice_strans_state).
+     * @param curr         Current state (pj_ice_strans_state).
+     */
+    void    (*on_ice_state)(pj_ice_strans *ice_st, int prev, int curr);

 } pj_ice_strans_cb;

--- pjsip_orig/pjnath/src/pjnath/ice_strans.c
+++ pjsip/pjnath/src/pjnath/ice_strans.c
@@ -240,6 +240,19 @@
 } sock_user_data;


+/* Set ICE state */
+static void set_ice_state(pj_ice_strans *ice_st, pj_ice_strans_state state)
+{
+    pj_ice_strans_state prev = ice_st->state;
+
+    if (prev != state) {
+        ice_st->state = state;
+        if (ice_st->cb.on_ice_state)
+            (*ice_st->cb.on_ice_state)(ice_st, prev, state);
+    }
+}
+
+
 /* Validate configuration */
 static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg)
 {
@@ -928,7 +941,7 @@
                    pj_pool_calloc(pool, comp_cnt, sizeof(pj_ice_strans_comp*));

     /* Move state to candidate gathering */
-    ice_st->state = PJ_ICE_STRANS_STATE_INIT;
+    set_ice_state(ice_st, PJ_ICE_STRANS_STATE_INIT);

     /* Acquire initialization mutex to prevent callback to be
      * called before we finish initialization.
@@ -1123,7 +1136,7 @@
      * candidate for a component.
      */
     ice_st->cb_called = PJ_TRUE;
-    ice_st->state = PJ_ICE_STRANS_STATE_READY;
+    set_ice_state(ice_st, PJ_ICE_STRANS_STATE_READY);
     if (ice_st->cb.on_ice_complete)
         (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_INIT,
                                       status);
@@ -1289,7 +1302,7 @@
     }

     /* ICE session is ready for negotiation */
-    ice_st->state = PJ_ICE_STRANS_STATE_SESS_READY;
+    set_ice_state(ice_st, PJ_ICE_STRANS_STATE_SESS_READY);

     return PJ_SUCCESS;

@@ -1518,7 +1531,7 @@
         return status;
     }

-    ice_st->state = PJ_ICE_STRANS_STATE_NEGO;
+    set_ice_state(ice_st, PJ_ICE_STRANS_STATE_NEGO);
     return status;
 }

@@ -1556,7 +1588,7 @@
         ice_st->ice = NULL;
     }

-    ice_st->state = PJ_ICE_STRANS_STATE_INIT;
+    set_ice_state(ice_st, PJ_ICE_STRANS_STATE_INIT);

     pj_grp_lock_release(ice_st->grp_lock);

--- pjsip_orig/pjnath/src/pjnath/ice_strans.c
+++ pjsip/pjnath/src/pjnath/ice_strans.c
@@ -1610,6 +1610,24 @@
     return pj_ice_sess_change_role(ice_st->ice, new_role);
 }
 
+/*
+ * Get ICE session.
+ */
+PJ_DEF(pj_ice_sess*)
+pj_ice_strans_get_session(const pj_ice_strans *ice_st)
+{
+    return ice_st->ice;
+}
+
+/*
+ * Get ICE start time.
+ */
+PJ_DEF(pj_time_val)
+pj_ice_strans_get_start_time(const pj_ice_strans *ice_st)
+{
+    return ice_st->start_time;
+}
+
 static pj_status_t setup_turn_perm( pj_ice_strans *ice_st)
 {
     unsigned n;
@@ -1877,8 +1909,8 @@
             }
         }

-        ice_st->state = (status==PJ_SUCCESS) ? PJ_ICE_STRANS_STATE_RUNNING :
-                                               PJ_ICE_STRANS_STATE_FAILED;
+        set_ice_state(ice_st, (status==PJ_SUCCESS) ? PJ_ICE_STRANS_STATE_RUNNING :
+                                                     PJ_ICE_STRANS_STATE_FAILED);

         pj_log_push_indent();
         (*cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION, status);
--- pjsip_orig/pjnath/include/pjnath/ice_strans.h
+++ pjsip/pjnath/include/pjnath/ice_strans.h
@@ -765,6 +765,29 @@
  * @return              The group lock.
  */
 PJ_DECL(pj_grp_lock_t *) pj_ice_strans_get_grp_lock(pj_ice_strans *ice_st);

+/**
+ * Retrieve the ICE session associated with this transport.
+ *
+ * @param ice_st       The ICE stream transport.
+ *
+ * @return             The ICE session, or NULL if no session has
+ *                     been initialised yet.
+ */
+PJ_DECL(pj_ice_sess*)
+pj_ice_strans_get_session(const pj_ice_strans *ice_st);
+
+/**
+ * Retrieve the timestamp at which ICE negotiation started.
+ *
+ * @param ice_st       The ICE stream transport.
+ *
+ * @return             The ICE start time (pj_gettimeofday() value
+ *                     captured at pj_ice_strans_start_ice). Returns
+ *                     {0,0} if ICE hasn't been started yet.
+ */
+PJ_DECL(pj_time_val)
+pj_ice_strans_get_start_time(const pj_ice_strans *ice_st);
+
 /**
  * Initialize the ICE session in the ICE stream transport.
