Merge "pjsip: Fix a few media bugs with reinvites and asymmetric payloads."
authorJoshua Colp <jcolp@digium.com>
Fri, 28 Oct 2016 00:37:47 +0000 (19:37 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 28 Oct 2016 00:37:47 +0000 (19:37 -0500)
1  2 
CHANGES
channels/chan_pjsip.c

diff --combined CHANGES
+++ b/CHANGES
@@@ -34,16 -34,6 +34,16 @@@ res_pjsi
     preferred codec rather than advertising all joint codec capabilities.
     This limits the other side's codec choice to exactly what we prefer.
  
 +cdr_radius
 +------------------
 + * To fix a memory leak the syslog channel is now empty if it has not been set
 +   and used by a syslog channel in the logger.
 +
 +cel_radius
 +------------------
 + * To fix a memory leak the syslog channel is now empty if it has not been set
 +   and used by a syslog channel in the logger.
 +
  ------------------------------------------------------------------------------
  --- Functionality changes from Asterisk 14.1.0 to Asterisk 14.2.0 ----------
  ------------------------------------------------------------------------------
@@@ -57,6 -47,13 +57,13 @@@ res_pjsi
     res_pjsip_multihomed module has also been moved into core res_pjsip to ensure
     that messages are updated with the correct address information in all cases.
  
+ chan_pjsip
+ ------------------
+  * The default behavior for RTP codecs has been changed. The sending codec will
+    now match the receiving codec. This can be turned off and behavior reverted
+    to asymmetric using the "asymmetric_rtp_codec" endpoint option. If this
+    option is set then the sending and received codec are allowed to differ.
  ------------------------------------------------------------------------------
  --- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ----------
  ------------------------------------------------------------------------------
diff --combined channels/chan_pjsip.c
@@@ -219,9 -219,7 +219,7 @@@ static enum ast_rtp_glue_result chan_pj
  /*! \brief Function called by RTP engine to get peer capabilities */
  static void chan_pjsip_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
  {
-       struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
-       ast_format_cap_append_from_cap(result, channel->session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
+       ast_format_cap_append_from_cap(result, ast_channel_nativeformats(chan), AST_MEDIA_TYPE_UNKNOWN);
  }
  
  /*! \brief Destructor function for \ref transport_info_data */
@@@ -559,12 -557,6 +557,12 @@@ static int answer(void *data
        struct ast_sip_session *session = data;
  
        if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      session->inv_session->cause,
 +                      pjsip_get_status_text(session->inv_session->cause)->ptr);
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              pjsip_inv_dec_ref(session->inv_session);
 +#endif
                return 0;
        }
  
                ast_sip_session_send_response(session, packet);
        }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +
        return (status == PJ_SUCCESS) ? 0 : -1;
  }
  
@@@ -601,23 -589,12 +599,23 @@@ static int chan_pjsip_answer(struct ast
        ast_setstate(ast, AST_STATE_UP);
        session = ao2_bump(channel->session);
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      if (pjsip_inv_add_ref(session->inv_session) != PJ_SUCCESS) {
 +              ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +              ao2_ref(session, -1);
 +              return -1;
 +      }
 +#endif
 +
        /* the answer task needs to be pushed synchronously otherwise a race condition
           can occur between this thread and bridging (specifically when native bridging
           attempts to do direct media) */
        ast_channel_unlock(ast);
        if (ast_sip_push_task_synchronous(session->serializer, answer, session)) {
                ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n");
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              pjsip_inv_dec_ref(session->inv_session);
 +#endif
                ao2_ref(session, -1);
                ast_channel_lock(ast);
                return -1;
@@@ -725,15 -702,28 +723,28 @@@ static struct ast_frame *chan_pjsip_rea
  
        session = channel->session;
  
-       if (ast_format_cap_iscompatible_format(session->endpoint->media.codecs, f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
-               ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when endpoint '%s' is not configured for it\n",
-                       ast_format_get_name(f->subclass.format), ast_channel_name(ast),
-                       ast_sorcery_object_get_id(session->endpoint));
+       if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+               ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when it has not been negotiated\n",
+                       ast_format_get_name(f->subclass.format), ast_channel_name(ast));
  
                ast_frfree(f);
                return &ast_null_frame;
        }
  
+       if (!session->endpoint->asymmetric_rtp_codec &&
+               ast_format_cmp(ast_channel_rawwriteformat(ast), f->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
+               /* For maximum compatibility we ensure that the write format matches that of the received media */
+               ast_debug(1, "Oooh, got a frame with format of %s on channel '%s' when we're sending '%s', switching to match\n",
+                       ast_format_get_name(f->subclass.format), ast_channel_name(ast),
+                       ast_format_get_name(ast_channel_rawwriteformat(ast)));
+               ast_channel_set_rawwriteformat(ast, f->subclass.format);
+               ast_set_write_format(ast, ast_channel_writeformat(ast));
+               if (ast_channel_is_bridged(ast)) {
+                       ast_channel_set_unbridged_nolock(ast, 1);
+               }
+       }
        if (session->dsp) {
                int dsp_features;
  
@@@ -1126,9 -1116,6 +1137,9 @@@ static int indicate(void *data
                ast_sip_session_send_response(session, packet);
        }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
        ao2_ref(ind_data, -1);
  
        return 0;
@@@ -1156,35 -1143,17 +1167,35 @@@ static int transmit_info_with_vidupdate
        RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
        struct pjsip_tx_data *tdata;
  
 +      if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      session->inv_session->cause,
 +                      pjsip_get_status_text(session->inv_session->cause)->ptr);
 +              goto failure;
 +      }
 +
        if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
                ast_log(LOG_ERROR, "Could not create text video update INFO request\n");
 -              return -1;
 +              goto failure;
        }
        if (ast_sip_add_body(tdata, &body)) {
                ast_log(LOG_ERROR, "Could not add body to text video update INFO request\n");
 -              return -1;
 +              goto failure;
        }
        ast_sip_session_send_request(session, tdata);
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +
        return 0;
 +
 +failure:
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +      return -1;
 +
  }
  
  /*!
@@@ -1227,17 -1196,6 +1238,17 @@@ static int update_connected_line_inform
  {
        struct ast_sip_session *session = data;
  
 +      if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      session->inv_session->cause,
 +                      pjsip_get_status_text(session->inv_session->cause)->ptr);
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +              ao2_ref(session, -1);
 +              return -1;
 +      }
 +
        if (ast_channel_state(session->channel) == AST_STATE_UP
                || session->inv_session->role == PJSIP_ROLE_UAC) {
                if (is_colp_update_allowed(session)) {
                }
        }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +
        ao2_ref(session, -1);
        return 0;
  }
@@@ -1392,18 -1346,10 +1403,18 @@@ static int chan_pjsip_indicate(struct a
                                res = ast_rtp_instance_write(media->rtp, &fr);
                        } else {
                                ao2_ref(channel->session, +1);
 -
 -                              if (ast_sip_push_task(channel->session->serializer, transmit_info_with_vidupdate, channel->session)) {
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +                              if (pjsip_inv_add_ref(channel->session->inv_session) != PJ_SUCCESS) {
 +                                      ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
                                        ao2_cleanup(channel->session);
 +                              } else {
 +#endif
 +                                      if (ast_sip_push_task(channel->session->serializer, transmit_info_with_vidupdate, channel->session)) {
 +                                              ao2_cleanup(channel->session);
 +                                      }
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
                                }
 +#endif
                        }
                        ast_test_suite_event_notify("AST_CONTROL_VIDUPDATE", "Result: Success");
                } else {
                break;
        case AST_CONTROL_CONNECTED_LINE:
                ao2_ref(channel->session, +1);
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              if (pjsip_inv_add_ref(channel->session->inv_session) != PJ_SUCCESS) {
 +                      ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +                      ao2_cleanup(channel->session);
 +                      return -1;
 +              }
 +#endif
                if (ast_sip_push_task(channel->session->serializer, update_connected_line_information, channel->session)) {
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +                      pjsip_inv_dec_ref(channel->session->inv_session);
 +#endif
                        ao2_cleanup(channel->session);
                }
                break;
  
        if (response_code) {
                struct indicate_data *ind_data = indicate_data_alloc(channel->session, condition, response_code, data, datalen);
 -              if (!ind_data || ast_sip_push_task(channel->session->serializer, indicate, ind_data)) {
 +
 +              if (!ind_data) {
 +                      return -1;
 +              }
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              if (pjsip_inv_add_ref(ind_data->session->inv_session) != PJ_SUCCESS) {
 +                      ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +                      ao2_cleanup(ind_data);
 +                      return -1;
 +              }
 +#endif
 +              if (ast_sip_push_task(channel->session->serializer, indicate, ind_data)) {
                        ast_log(LOG_NOTICE, "Cannot send response code %d to endpoint %s. Could not queue task properly\n",
                                        response_code, ast_sorcery_object_get_id(channel->session->endpoint));
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +                      pjsip_inv_dec_ref(ind_data->session->inv_session);
 +#endif
                        ao2_cleanup(ind_data);
                        res = -1;
                }
@@@ -1653,31 -1575,21 +1664,31 @@@ static int transfer(void *data
        struct ast_sip_contact *contact = NULL;
        const char *target = trnf_data->target;
  
 -      /* See if we have an endpoint; if so, use its contact */
 -      endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", target);
 -      if (endpoint) {
 -              contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
 -              if (contact && !ast_strlen_zero(contact->uri)) {
 -                      target = contact->uri;
 +      if (trnf_data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      trnf_data->session->inv_session->cause,
 +                      pjsip_get_status_text(trnf_data->session->inv_session->cause)->ptr);
 +      } else {
 +              /* See if we have an endpoint; if so, use its contact */
 +              endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", target);
 +              if (endpoint) {
 +                      contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
 +                      if (contact && !ast_strlen_zero(contact->uri)) {
 +                              target = contact->uri;
 +                      }
                }
 -      }
  
 -      if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
 -              transfer_redirect(trnf_data->session, target);
 -      } else {
 -              transfer_refer(trnf_data->session, target);
 +              if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
 +                      transfer_redirect(trnf_data->session, target);
 +              } else {
 +                      transfer_refer(trnf_data->session, target);
 +              }
        }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(trnf_data->session->inv_session);
 +#endif
 +
        ao2_ref(trnf_data, -1);
        ao2_cleanup(endpoint);
        ao2_cleanup(contact);
@@@ -1694,19 -1606,8 +1705,19 @@@ static int chan_pjsip_transfer(struct a
                return -1;
        }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      if (pjsip_inv_add_ref(trnf_data->session->inv_session) != PJ_SUCCESS) {
 +              ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +              ao2_cleanup(trnf_data);
 +              return -1;
 +      }
 +#endif
 +
        if (ast_sip_push_task(channel->session->serializer, transfer, trnf_data)) {
                ast_log(LOG_WARNING, "Error requesting transfer\n");
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              pjsip_inv_dec_ref(trnf_data->session->inv_session);
 +#endif
                ao2_cleanup(trnf_data);
                return -1;
        }
@@@ -1788,16 -1689,9 +1799,16 @@@ static int transmit_info_dtmf(void *dat
                .subtype = "dtmf-relay",
        };
  
 +      if (session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      session->inv_session->cause,
 +                      pjsip_get_status_text(session->inv_session->cause)->ptr);
 +              goto failure;
 +      }
 +
        if (!(body_text = ast_str_create(32))) {
                ast_log(LOG_ERROR, "Could not allocate buffer for INFO DTMF.\n");
 -              return -1;
 +              goto failure;
        }
        ast_str_set(&body_text, 0, "Signal=%c\r\nDuration=%u\r\n", dtmf_data->digit, dtmf_data->duration);
  
  
        if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
                ast_log(LOG_ERROR, "Could not create DTMF INFO request\n");
 -              return -1;
 +              goto failure;
        }
        if (ast_sip_add_body(tdata, &body)) {
                ast_log(LOG_ERROR, "Could not add body to DTMF INFO request\n");
                pjsip_tx_data_dec_ref(tdata);
 -              return -1;
 +              goto failure;
        }
        ast_sip_session_send_request(session, tdata);
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +
        return 0;
 +
 +failure:
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(session->inv_session);
 +#endif
 +      return -1;
 +
  }
  
  /*! \brief Function called by core to stop a DTMF digit */
@@@ -1845,19 -1728,8 +1856,19 @@@ static int chan_pjsip_digit_end(struct 
                        return -1;
                }
  
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              if (pjsip_inv_add_ref(dtmf_data->session->inv_session) != PJ_SUCCESS) {
 +                      ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +                      ao2_cleanup(dtmf_data);
 +                      return -1;
 +              }
 +#endif
 +
                if (ast_sip_push_task(channel->session->serializer, transmit_info_dtmf, dtmf_data)) {
                        ast_log(LOG_WARNING, "Error sending DTMF via INFO.\n");
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +                      pjsip_inv_dec_ref(dtmf_data->session->inv_session);
 +#endif
                        ao2_cleanup(dtmf_data);
                        return -1;
                }
@@@ -2203,21 -2075,11 +2214,21 @@@ static int sendtext(void *obj
                .body_text = data->text
        };
  
 -      ast_debug(3, "Sending in dialog SIP message\n");
 +      if (data->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
 +              ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n",
 +                      data->session->inv_session->cause,
 +                      pjsip_get_status_text(data->session->inv_session->cause)->ptr);
 +      } else {
 +              ast_debug(3, "Sending in dialog SIP message\n");
 +
 +              ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
 +              ast_sip_add_body(tdata, &body);
 +              ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
 +      }
  
 -      ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
 -      ast_sip_add_body(tdata, &body);
 -      ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      pjsip_inv_dec_ref(data->session->inv_session);
 +#endif
  
        return 0;
  }
@@@ -2228,22 -2090,7 +2239,22 @@@ static int chan_pjsip_sendtext(struct a
        struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(ast);
        struct sendtext_data *data = sendtext_data_create(channel->session, text);
  
 -      if (!data || ast_sip_push_task(channel->session->serializer, sendtext, data)) {
 +      if (!data) {
 +              return -1;
 +      }
 +
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +      if (pjsip_inv_add_ref(data->session->inv_session) != PJ_SUCCESS) {
 +              ast_log(LOG_ERROR, "Can't increase the session reference counter\n");
 +              ao2_ref(data, -1);
 +              return -1;
 +      }
 +#endif
 +
 +      if (ast_sip_push_task(channel->session->serializer, sendtext, data)) {
 +#ifdef HAVE_PJSIP_INV_SESSION_REF
 +              pjsip_inv_dec_ref(data->session->inv_session);
 +#endif
                ao2_ref(data, -1);
                return -1;
        }