Esempio n. 1
0
void Sdp::startNegotiation()
{
    if (negotiator_ == NULL) {
        ERROR("Can't start negotiation with invalid negotiator");
        return;
    }

    const pjmedia_sdp_session *active_local;
    const pjmedia_sdp_session *active_remote;

    if (pjmedia_sdp_neg_get_state(negotiator_) != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
        WARN("Negotiator not in right state for negotiation");

    if (pjmedia_sdp_neg_negotiate(memPool_, negotiator_, 0) != PJ_SUCCESS)
        return;

    if (pjmedia_sdp_neg_get_active_local(negotiator_, &active_local) != PJ_SUCCESS)
        ERROR("Could not retrieve local active session");
    else
        setActiveLocalSdpSession(active_local);

    if (pjmedia_sdp_neg_get_active_remote(negotiator_, &active_remote) != PJ_SUCCESS)
        ERROR("Could not retrieve remote active session");
    else
        setActiveRemoteSdpSession(active_remote);
}
Esempio n. 2
0
/*
 * Callback when SDP negotiation has completed.
 * We are interested with this callback because we want to start media
 * as soon as SDP negotiation is completed.
 */
static void call_on_media_update( pjsip_inv_session *inv,
				  pj_status_t status)
{
    pjmedia_stream_info stream_info;
    const pjmedia_sdp_session *local_sdp;
    const pjmedia_sdp_session *remote_sdp;
    pjmedia_port *media_port;

    if (status != PJ_SUCCESS) {

	app_perror(THIS_FILE, "SDP negotiation has failed", status);

	/* Here we should disconnect call if we're not in the middle 
	 * of initializing an UAS dialog and if this is not a re-INVITE.
	 */
	return;
    }

    /* Get local and remote SDP.
     * We need both SDPs to create a media session.
     */
    status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);

    status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);


    /* Create stream info based on the media audio SDP. */
    status = pjmedia_stream_info_from_sdp(&stream_info, inv->dlg->pool,
					  g_med_endpt,
					  local_sdp, remote_sdp, 0);
    if (status != PJ_SUCCESS) {
	app_perror(THIS_FILE,"Unable to create audio stream info",status);
	return;
    }

    /* If required, we can also change some settings in the stream info,
     * (such as jitter buffer settings, codec settings, etc) before we
     * create the stream.
     */

    /* Create new audio media stream, passing the stream info, and also the
     * media socket that we created earlier.
     */
    status = pjmedia_stream_create(g_med_endpt, inv->dlg->pool, &stream_info,
				   g_med_transport[0], NULL, &g_med_stream);
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create audio stream", status);
	return;
    }

    /* Start the audio stream */
    status = pjmedia_stream_start(g_med_stream);
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to start audio stream", status);
	return;
    }

    /* Get the media port interface of the audio stream. 
     * Media port interface is basicly a struct containing get_frame() and
     * put_frame() function. With this media port interface, we can attach
     * the port interface to conference bridge, or directly to a sound
     * player/recorder device.
     */
    pjmedia_stream_get_port(g_med_stream, &media_port);

    /* Create sound port */
    pjmedia_snd_port_create(inv->pool,
                            PJMEDIA_AUD_DEFAULT_CAPTURE_DEV,
                            PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV,
                            PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate	    */
                            PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */
                            PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/
                            PJMEDIA_PIA_BITS(&media_port->info),/* bits per sample  */
                            0,
                            &g_snd_port);

    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create sound port", status);
	PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
		    PJMEDIA_PIA_SRATE(&media_port->info),/* clock rate	    */
		    PJMEDIA_PIA_CCNT(&media_port->info),/* channel count    */
		    PJMEDIA_PIA_SPF(&media_port->info), /* samples per frame*/
		    PJMEDIA_PIA_BITS(&media_port->info) /* bits per sample  */
	    ));
	return;
    }

    status = pjmedia_snd_port_connect(g_snd_port, media_port);


    /* Get the media port interface of the second stream in the session,
     * which is video stream. With this media port interface, we can attach
     * the port directly to a renderer/capture video device.
     */
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
    if (local_sdp->media_count > 1) {
	pjmedia_vid_stream_info vstream_info;
	pjmedia_vid_port_param vport_param;

	pjmedia_vid_port_param_default(&vport_param);

	/* Create stream info based on the media video SDP. */
	status = pjmedia_vid_stream_info_from_sdp(&vstream_info,
						  inv->dlg->pool, g_med_endpt,
						  local_sdp, remote_sdp, 1);
	if (status != PJ_SUCCESS) {
	    app_perror(THIS_FILE,"Unable to create video stream info",status);
	    return;
	}

	/* If required, we can also change some settings in the stream info,
	 * (such as jitter buffer settings, codec settings, etc) before we
	 * create the video stream.
	 */

	/* Create new video media stream, passing the stream info, and also the
	 * media socket that we created earlier.
	 */
	status = pjmedia_vid_stream_create(g_med_endpt, NULL, &vstream_info,
	                                   g_med_transport[1], NULL,
	                                   &g_med_vstream);
	if (status != PJ_SUCCESS) {
	    app_perror( THIS_FILE, "Unable to create video stream", status);
	    return;
	}

	/* Start the video stream */
	status = pjmedia_vid_stream_start(g_med_vstream);
	if (status != PJ_SUCCESS) {
	    app_perror( THIS_FILE, "Unable to start video stream", status);
	    return;
	}

	if (vstream_info.dir & PJMEDIA_DIR_DECODING) {
	    status = pjmedia_vid_dev_default_param(
				inv->pool, PJMEDIA_VID_DEFAULT_RENDER_DEV,
				&vport_param.vidparam);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to get default param of video "
			   "renderer device", status);
		return;
	    }

	    /* Get video stream port for decoding direction */
	    pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_DECODING,
					&media_port);

	    /* Set format */
	    pjmedia_format_copy(&vport_param.vidparam.fmt,
				&media_port->info.fmt);
	    vport_param.vidparam.dir = PJMEDIA_DIR_RENDER;
	    vport_param.active = PJ_TRUE;

	    /* Create renderer */
	    status = pjmedia_vid_port_create(inv->pool, &vport_param, 
					     &g_vid_renderer);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to create video renderer device",
			   status);
		return;
	    }

	    /* Connect renderer to media_port */
	    status = pjmedia_vid_port_connect(g_vid_renderer, media_port, 
					      PJ_FALSE);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to connect renderer to stream",
			   status);
		return;
	    }
	}

	/* Create capturer */
	if (vstream_info.dir & PJMEDIA_DIR_ENCODING) {
	    status = pjmedia_vid_dev_default_param(
				inv->pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
				&vport_param.vidparam);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to get default param of video "
			   "capture device", status);
		return;
	    }

	    /* Get video stream port for decoding direction */
	    pjmedia_vid_stream_get_port(g_med_vstream, PJMEDIA_DIR_ENCODING,
					&media_port);

	    /* Get capturer format from stream info */
	    pjmedia_format_copy(&vport_param.vidparam.fmt, 
	                        &media_port->info.fmt);
	    vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
	    vport_param.active = PJ_TRUE;

	    /* Create capturer */
	    status = pjmedia_vid_port_create(inv->pool, &vport_param, 
					     &g_vid_capturer);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to create video capture device",
			   status);
		return;
	    }

	    /* Connect capturer to media_port */
	    status = pjmedia_vid_port_connect(g_vid_capturer, media_port, 
					      PJ_FALSE);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to connect capturer to stream",
			   status);
		return;
	    }
	}

	/* Start streaming */
	if (g_vid_renderer) {
	    status = pjmedia_vid_port_start(g_vid_renderer);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to start video renderer",
			   status);
		return;
	    }
	}
	if (g_vid_capturer) {
	    status = pjmedia_vid_port_start(g_vid_capturer);
	    if (status != PJ_SUCCESS) {
		app_perror(THIS_FILE, "Unable to start video capturer",
			   status);
		return;
	    }
	}
    }
#endif	/* PJMEDIA_HAS_VIDEO */

    /* Done with media. */
}
Esempio n. 3
0
static int offer_answer_test(pj_pool_t *pool, pjmedia_sdp_neg **p_neg,
			     struct offer_answer *oa)
{
    pjmedia_sdp_session *sdp1;
    pjmedia_sdp_neg *neg;
    pj_status_t status;

    status = pjmedia_sdp_parse(pool, oa->sdp1, pj_ansi_strlen(oa->sdp1),
				&sdp1);
    if (status != PJ_SUCCESS) {
	app_perror(status, "   error: unexpected parse status for sdp1");
	return -10;
    }

    status = pjmedia_sdp_validate(sdp1);
    if (status != PJ_SUCCESS) {
	app_perror(status, "   error: sdp1 validation failed");
	return -15;
    }

    neg = *p_neg;

    if (oa->type == LOCAL_OFFER) {
	
	/* 
	 * Local creates offer first. 
	 */
	pjmedia_sdp_session *sdp2, *sdp3;
	const pjmedia_sdp_session *active;

	if (neg == NULL) {
	    /* Create negotiator with local offer. */
	    status = pjmedia_sdp_neg_create_w_local_offer(pool, sdp1, &neg);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: pjmedia_sdp_neg_create_w_local_offer");
		return -20;
	    }
	    *p_neg = neg;

	} else {
	    /* Modify local offer */
	    status = pjmedia_sdp_neg_modify_local_offer(pool, neg, sdp1);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: pjmedia_sdp_neg_modify_local_offer");
		return -30;
	    }
	}

	/* Parse and validate remote answer */
	status = pjmedia_sdp_parse(pool, oa->sdp2, pj_ansi_strlen(oa->sdp2),
				   &sdp2);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: parsing sdp2");
	    return -40;
	}

	status = pjmedia_sdp_validate(sdp2);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: sdp2 validation failed");
	    return -50;
	}

	/* Give the answer to negotiator. */
	status = pjmedia_sdp_neg_set_remote_answer(pool, neg, sdp2);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: pjmedia_sdp_neg_rx_remote_answer");
	    return -60;
	}

	/* Negotiate remote answer with local answer */
	status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: pjmedia_sdp_neg_negotiate");
	    return -70;
	}

	/* Get the local active media. */
	status = pjmedia_sdp_neg_get_active_local(neg, &active);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: pjmedia_sdp_neg_get_local");
	    return -80;
	}

	/* Parse and validate the correct active media. */
	status = pjmedia_sdp_parse(pool, oa->sdp3, pj_ansi_strlen(oa->sdp3),
				   &sdp3);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: parsing sdp3");
	    return -90;
	}

	status = pjmedia_sdp_validate(sdp3);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: sdp3 validation failed");
	    return -100;
	}

	/* Compare active with sdp3 */
	status = pjmedia_sdp_session_cmp(active, sdp3, 0);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: active local comparison mismatch");
	    compare_sdp_string("Logical cmp after negotiatin remote answer",
			       "Active local sdp from negotiator", active,
			       "The correct active local sdp", sdp3,
			       status);
	    return -110;
	}

	/* Compare the string representation oa both sdps */
	status = compare_sdp_string("String cmp after negotiatin remote answer",
				    "Active local sdp from negotiator", active,
				    "The correct active local sdp", sdp3,
				    PJ_SUCCESS);
	if (status != 0)
	    return -120;

    } else {
	/* 
	 * Remote creates offer first. 
	 */

	pjmedia_sdp_session *sdp2 = NULL, *sdp3;
	const pjmedia_sdp_session *answer;

	if (oa->sdp2) {
	    /* Parse and validate initial local capability */
	    status = pjmedia_sdp_parse(pool, oa->sdp2, pj_ansi_strlen(oa->sdp2),
				       &sdp2);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: parsing sdp2");
		return -200;
	    }

	    status = pjmedia_sdp_validate(sdp2);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: sdp2 validation failed");
		return -210;
	    }
	} else if (neg) {
	    status = pjmedia_sdp_neg_get_active_local(neg, &sdp2);
	    if (status != PJ_SUCCESS) {
		app_perror(status, 
			   "   error: pjmedia_sdp_neg_get_active_local");
		return -215;
	    }
	}

	if (neg == NULL) {
	    /* Create negotiator with remote offer. */
	    status = pjmedia_sdp_neg_create_w_remote_offer(pool, sdp2, sdp1, &neg);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: pjmedia_sdp_neg_create_w_remote_offer");
		return -220;
	    }
	    *p_neg = neg;

	} else {
	    /* Received subsequent offer from remote. */
	    status = pjmedia_sdp_neg_set_remote_offer(pool, neg, sdp1);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: pjmedia_sdp_neg_rx_remote_offer");
		return -230;
	    }

	    status = pjmedia_sdp_neg_set_local_answer(pool, neg, sdp2);
	    if (status != PJ_SUCCESS) {
		app_perror(status, "   error: pjmedia_sdp_neg_set_local_answer");
		return -235;
	    }
	}

	/* Negotiate. */
	status = pjmedia_sdp_neg_negotiate(pool, neg, 0);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: pjmedia_sdp_neg_negotiate");
	    return -240;
	}
	
	/* Get our answer. */
	status = pjmedia_sdp_neg_get_active_local(neg, &answer);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: pjmedia_sdp_neg_get_local");
	    return -250;
	}

	/* Parse the correct answer. */
	status = pjmedia_sdp_parse(pool, oa->sdp3, pj_ansi_strlen(oa->sdp3),
				   &sdp3);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: parsing sdp3");
	    return -260;
	}

	/* Validate the correct answer. */
	status = pjmedia_sdp_validate(sdp3);
	if (status != PJ_SUCCESS) {
	    app_perror(status, "   error: sdp3 validation failed");
	    return -270;
	}

	/* Compare answer from negotiator and the correct answer */
	status = pjmedia_sdp_session_cmp(sdp3, answer, 0);
	if (status != PJ_SUCCESS) {
	    compare_sdp_string("Logical cmp after negotiating remote offer",
			       "Local answer from negotiator", answer,
			       "The correct local answer", sdp3,
			       status);

	    return -280;
	}

	/* Compare the string representation oa both answers */
	status = compare_sdp_string("String cmp after negotiating remote offer",
				    "Local answer from negotiator", answer,
				    "The correct local answer", sdp3,
				    PJ_SUCCESS);
	if (status != 0)
	    return -290;

    }

    return 0;
}
Esempio n. 4
0
/*
 * Callback when SDP negotiation has completed.
 * We are interested with this callback because we want to start media
 * as soon as SDP negotiation is completed.
 */
static void call_on_media_update( pjsip_inv_session *inv,
				  pj_status_t status)
{
    pjmedia_session_info sess_info;
    const pjmedia_sdp_session *local_sdp;
    const pjmedia_sdp_session *remote_sdp;
    pjmedia_port *media_port;

    if (status != PJ_SUCCESS) {

	app_perror(THIS_FILE, "SDP negotiation has failed", status);

	/* Here we should disconnect call if we're not in the middle 
	 * of initializing an UAS dialog and if this is not a re-INVITE.
	 */
	return;
    }

    /* Get local and remote SDP.
     * We need both SDPs to create a media session.
     */
    status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);

    status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);


    /* Create session info based on the two SDPs. 
     * We only support one stream per session for now.
     */
    status = pjmedia_session_info_from_sdp(inv->dlg->pool, g_med_endpt, 
					   1, &sess_info, 
					   local_sdp, remote_sdp);
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create media session", status);
	return;
    }

    /* If required, we can also change some settings in the session info,
     * (such as jitter buffer settings, codec settings, etc) before we
     * create the session.
     */

    /* Create new media session, passing the two SDPs, and also the
     * media socket that we created earlier.
     * The media session is active immediately.
     */
    status = pjmedia_session_create( g_med_endpt, &sess_info,
				     &g_med_transport, NULL, &g_med_session );
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create media session", status);
	return;
    }


    /* Get the media port interface of the first stream in the session. 
     * Media port interface is basicly a struct containing get_frame() and
     * put_frame() function. With this media port interface, we can attach
     * the port interface to conference bridge, or directly to a sound
     * player/recorder device.
     */
    pjmedia_session_get_port(g_med_session, 0, &media_port);



    /* Create a sound Player device and connect the media port to the
     * sound device.
     */
    status = pjmedia_snd_port_create_player( 
		    inv->pool,				/* pool		    */
		    -1,					/* sound dev id	    */
		    media_port->info.clock_rate,	/* clock rate	    */
		    media_port->info.channel_count,	/* channel count    */
		    media_port->info.samples_per_frame, /* samples per frame*/
		    media_port->info.bits_per_sample,   /* bits per sample  */
		    0,					/* options	    */
		    &g_snd_player);
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create sound player", status);
	PJ_LOG(3,(THIS_FILE, "%d %d %d %d",
	    	    media_port->info.clock_rate,	/* clock rate	    */
		    media_port->info.channel_count,	/* channel count    */
		    media_port->info.samples_per_frame, /* samples per frame*/
		    media_port->info.bits_per_sample    /* bits per sample  */
	    ));
	return;
    }

    status = pjmedia_snd_port_connect(g_snd_player, media_port);


    /* Create a sound recorder device and connect the media port to the
     * sound device.
     */
    status = pjmedia_snd_port_create_rec( 
		    inv->pool,				/* pool		    */
		    -1,					/* sound dev id	    */
		    media_port->info.clock_rate,	/* clock rate	    */
		    media_port->info.channel_count,	/* channel count    */
		    media_port->info.samples_per_frame, /* samples per frame*/
		    media_port->info.bits_per_sample,   /* bits per sample  */
		    0,					/* options	    */
		    &g_snd_rec);
    if (status != PJ_SUCCESS) {
	app_perror( THIS_FILE, "Unable to create sound recorder", status);
	return;
    }

    status = pjmedia_snd_port_connect(g_snd_rec, media_port);

    /* Done with media. */
}
Esempio n. 5
0
/* Timer callback. When the timer is fired, it can be time to refresh
 * the session if UA is the refresher, otherwise it is time to end 
 * the session.
 */
static void timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
{
    pjsip_inv_session *inv = (pjsip_inv_session*) entry->user_data;
    pjsip_tx_data *tdata = NULL;
    pj_status_t status;
    pj_bool_t as_refresher;

    pj_assert(inv);

    PJ_UNUSED_ARG(timer_heap);

    /* Lock dialog. */
    pjsip_dlg_inc_lock(inv->dlg);

    /* Check our role */
    as_refresher =
	(inv->timer->refresher == TR_UAC && inv->timer->role == PJSIP_ROLE_UAC) ||
	(inv->timer->refresher == TR_UAS && inv->timer->role == PJSIP_ROLE_UAS);    

    /* Do action based on role(refresher or refreshee). 
     * As refresher:
     * - send refresh, or  
     * - end session if there is no response to the refresh request.
     * As refreshee:
     * - end session if there is no refresh request received.
     */
    if (as_refresher && (entry->id != REFRESHER_EXPIRE_TIMER_ID)) {
	pj_time_val now;

	/* As refresher, reshedule the refresh request on the following:
	 *  - must not send re-INVITE if another INVITE or SDP negotiation
	 *    is in progress.
	 *  - must not send UPDATE with SDP if SDP negotiation is in progress
	 */
	pjmedia_sdp_neg_state neg_state = pjmedia_sdp_neg_get_state(inv->neg);

	inv->timer->timer.id = 0;

	if ( (!inv->timer->use_update && (
			inv->invite_tsx != NULL ||
			neg_state != PJMEDIA_SDP_NEG_STATE_DONE)
             )
	     ||
	     (inv->timer->use_update && inv->timer->with_sdp &&
		     neg_state != PJMEDIA_SDP_NEG_STATE_DONE
	     )
	   )
	{
	    pj_time_val delay = {1, 0};

	    inv->timer->timer.id = 1;
	    pjsip_endpt_schedule_timer(inv->dlg->endpt, &inv->timer->timer,
				       &delay);
	    pjsip_dlg_dec_lock(inv->dlg);
	    return;
	}

	/* Refresher, refresh the session */
	if (inv->timer->use_update) {
	    const pjmedia_sdp_session *offer = NULL;

	    if (inv->timer->with_sdp) {
		pjmedia_sdp_neg_get_active_local(inv->neg, &offer);
	    }
	    status = pjsip_inv_update(inv, NULL, offer, &tdata);
	} else {
	    /* Create re-INVITE without modifying session */
	    pjsip_msg_body *body;
	    const pjmedia_sdp_session *offer = NULL;

	    pj_assert(pjmedia_sdp_neg_get_state(inv->neg) == 
		      PJMEDIA_SDP_NEG_STATE_DONE);

	    status = pjsip_inv_invite(inv, &tdata);
	    if (status == PJ_SUCCESS)
		status = pjmedia_sdp_neg_send_local_offer(inv->pool_prov, 
							  inv->neg, &offer);
	    if (status == PJ_SUCCESS)
		status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
	    if (status == PJ_SUCCESS) {
		status = pjsip_create_sdp_body(tdata->pool, 
					(pjmedia_sdp_session*)offer, &body);
		tdata->msg->body = body;
	    }
	}

	pj_gettimeofday(&now);
	PJ_LOG(4, (inv->pool->obj_name,
		   "Refreshing session after %ds (expiration period=%ds)",
		   (now.sec-inv->timer->last_refresh.sec),
		   inv->timer->setting.sess_expires));
    } else {
	
	pj_time_val now;

	if (as_refresher)
	    inv->timer->expire_timer.id = 0;
	else
	    inv->timer->timer.id = 0;

	/* Terminate the session */
	status = pjsip_inv_end_session(inv, PJSIP_SC_REQUEST_TIMEOUT, 
				       NULL, &tdata);

	pj_gettimeofday(&now);
	PJ_LOG(3, (inv->pool->obj_name, 
		   "No session %s received after %ds "
		   "(expiration period=%ds), stopping session now!",
		   (as_refresher?"refresh response":"refresh"),
		   (now.sec-inv->timer->last_refresh.sec),
		   inv->timer->setting.sess_expires));

    }

    /* Unlock dialog. */
    pjsip_dlg_dec_lock(inv->dlg);

    /* Send message, if any */
    if (tdata && status == PJ_SUCCESS) {
	inv->timer->refresh_tdata = tdata;

	status = pjsip_inv_send_msg(inv, tdata);	
    }

    /* Print error message, if any */
    if (status != PJ_SUCCESS) {
	PJ_PERROR(2, (inv->pool->obj_name, status,
		     "Error in %s session timer",
		     ((as_refresher && entry->id != REFRESHER_EXPIRE_TIMER_ID)? 
		       "refreshing" : "terminating")));
    }
}