summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/samples/simpleua.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip-apps/src/samples/simpleua.c')
-rw-r--r--pjsip-apps/src/samples/simpleua.c443
1 files changed, 360 insertions, 83 deletions
diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c
index 38b13923..2ec49d37 100644
--- a/pjsip-apps/src/samples/simpleua.c
+++ b/pjsip-apps/src/samples/simpleua.c
@@ -67,11 +67,19 @@
/* Settings */
-#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
+#define AF pj_AF_INET() /* Change to pj_AF_INET6() for IPv6.
* PJ_HAS_IPV6 must be enabled and
* your system must support IPv6. */
-#define SIP_PORT 5060 /* Listening SIP port */
-#define RTP_PORT 4000 /* RTP port */
+#if 0
+#define SIP_PORT 5080 /* Listening SIP port */
+#define RTP_PORT 5000 /* RTP port */
+#else
+#define SIP_PORT 5060 /* Listening SIP port */
+#define RTP_PORT 4000 /* RTP port */
+#endif
+
+#define MAX_MEDIA_CNT 2 /* Media count, set to 1 for audio
+ * only or 2 for audio and video */
/*
* Static variables.
@@ -82,15 +90,24 @@ static pjsip_endpoint *g_endpt; /* SIP endpoint. */
static pj_caching_pool cp; /* Global pool factory. */
static pjmedia_endpt *g_med_endpt; /* Media endpoint. */
-static pjmedia_transport_info g_med_tpinfo; /* Socket info for media */
-static pjmedia_transport *g_med_transport;/* Media stream transport */
+
+static pjmedia_transport_info g_med_tpinfo[MAX_MEDIA_CNT];
+ /* Socket info for media */
+static pjmedia_transport *g_med_transport[MAX_MEDIA_CNT];
+ /* Media stream transport */
+static pjmedia_sock_info g_sock_info[MAX_MEDIA_CNT];
+ /* Socket info array */
/* Call variables: */
static pjsip_inv_session *g_inv; /* Current invite session. */
-static pjmedia_session *g_med_session; /* Call's media session. */
-static pjmedia_snd_port *g_snd_player; /* Call's sound player */
-static pjmedia_snd_port *g_snd_rec; /* Call's sound recorder. */
+static pjmedia_stream *g_med_stream; /* Call's audio stream. */
+static pjmedia_snd_port *g_snd_port; /* Sound device. */
+#if PJMEDIA_HAS_VIDEO
+static pjmedia_vid_stream *g_med_vstream; /* Call's video stream. */
+static pjmedia_vid_port *g_vid_capturer;/* Call's video capturer. */
+static pjmedia_vid_port *g_vid_renderer;/* Call's video renderer. */
+#endif /* PJMEDIA_HAS_VIDEO */
/*
* Prototypes:
@@ -136,6 +153,68 @@ static pjsip_module mod_simpleua =
};
+/* Notification on incoming messages */
+static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata)
+{
+ PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s %s:%d:\n"
+ "%.*s\n"
+ "--end msg--",
+ rdata->msg_info.len,
+ pjsip_rx_data_get_info(rdata),
+ rdata->tp_info.transport->type_name,
+ rdata->pkt_info.src_name,
+ rdata->pkt_info.src_port,
+ (int)rdata->msg_info.len,
+ rdata->msg_info.msg_buf));
+
+ /* Always return false, otherwise messages will not get processed! */
+ return PJ_FALSE;
+}
+
+/* Notification on outgoing messages */
+static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata)
+{
+
+ /* Important note:
+ * tp_info field is only valid after outgoing messages has passed
+ * transport layer. So don't try to access tp_info when the module
+ * has lower priority than transport layer.
+ */
+
+ PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s %s:%d:\n"
+ "%.*s\n"
+ "--end msg--",
+ (tdata->buf.cur - tdata->buf.start),
+ pjsip_tx_data_get_info(tdata),
+ tdata->tp_info.transport->type_name,
+ tdata->tp_info.dst_name,
+ tdata->tp_info.dst_port,
+ (int)(tdata->buf.cur - tdata->buf.start),
+ tdata->buf.start));
+
+ /* Always return success, otherwise message will not get sent! */
+ return PJ_SUCCESS;
+}
+
+/* The module instance. */
+static pjsip_module msg_logger =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-msg-log", 13 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &logging_on_rx_msg, /* on_rx_request() */
+ &logging_on_rx_msg, /* on_rx_response() */
+ &logging_on_tx_msg, /* on_tx_request. */
+ &logging_on_tx_msg, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
/*
* main()
@@ -145,12 +224,15 @@ static pjsip_module mod_simpleua =
*/
int main(int argc, char *argv[])
{
+ pj_pool_t *pool;
pj_status_t status;
+ unsigned i;
/* Must init PJLIB first: */
status = pj_init();
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+ pj_log_set_level(5);
/* Then init PJLIB-UTIL: */
status = pjlib_util_init();
@@ -262,6 +344,12 @@ int main(int argc, char *argv[])
status = pjsip_endpt_register_module( g_endpt, &mod_simpleua);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+ /*
+ * Register message logger module.
+ */
+ status = pjsip_endpt_register_module( g_endpt, &msg_logger);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
/*
* Initialize media endpoint.
@@ -284,27 +372,52 @@ int main(int argc, char *argv[])
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
#endif
+
+ /* Init video subsystem */
+#if PJMEDIA_HAS_VIDEO
+ pool = pjmedia_endpt_create_pool(g_med_endpt, "Video subsystem", 512, 512);
+ status = pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+ status = pjmedia_converter_mgr_create(pool, NULL);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+ status = pjmedia_vid_codec_mgr_create(pool, NULL);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+ status = pjmedia_vid_dev_subsys_init(&cp.factory);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+
+# if PJMEDIA_HAS_FFMPEG_CODEC
+ /* Init ffmpeg video codecs */
+ status = pjmedia_codec_ffmpeg_init(NULL, &cp.factory);
+ PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
+# endif /* PJMEDIA_HAS_FFMPEG_CODEC */
+
+#endif /* PJMEDIA_HAS_VIDEO */
/*
* Create media transport used to send/receive RTP/RTCP socket.
* One media transport is needed for each call. Application may
* opt to re-use the same media transport for subsequent calls.
*/
- status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL,
- RTP_PORT, 0, &g_med_transport);
- if (status != PJ_SUCCESS) {
- app_perror(THIS_FILE, "Unable to create media transport", status);
- return 1;
- }
+ for (i = 0; i < PJ_ARRAY_SIZE(g_med_transport); ++i) {
+ status = pjmedia_transport_udp_create3(g_med_endpt, AF, NULL, NULL,
+ RTP_PORT + i*2, 0,
+ &g_med_transport[i]);
+ if (status != PJ_SUCCESS) {
+ app_perror(THIS_FILE, "Unable to create media transport", status);
+ return 1;
+ }
- /*
- * Get socket info (address, port) of the media transport. We will
- * need this info to create SDP (i.e. the address and port info in
- * the SDP).
- */
- pjmedia_transport_info_init(&g_med_tpinfo);
- pjmedia_transport_get_info(g_med_transport, &g_med_tpinfo);
+ /*
+ * Get socket info (address, port) of the media transport. We will
+ * need this info to create SDP (i.e. the address and port info in
+ * the SDP).
+ */
+ pjmedia_transport_info_init(&g_med_tpinfo[i]);
+ pjmedia_transport_get_info(g_med_transport[i], &g_med_tpinfo[i]);
+ pj_memcpy(&g_sock_info[i], &g_med_tpinfo[i].sock_info,
+ sizeof(pjmedia_sock_info));
+ }
/*
* If URL is specified, then make call immediately.
@@ -360,15 +473,12 @@ int main(int argc, char *argv[])
/* Get the SDP body to be put in the outgoing INVITE, by asking
- * media endpoint to create one for us. The SDP will contain all
- * codecs that have been registered to it (in this case, only
- * PCMA and PCMU), plus telephony event.
+ * media endpoint to create one for us.
*/
status = pjmedia_endpt_create_sdp( g_med_endpt, /* the media endpt */
dlg->pool, /* pool. */
- 1, /* # of streams */
- &g_med_tpinfo.sock_info,
- /* RTP sock info */
+ MAX_MEDIA_CNT, /* # of streams */
+ g_sock_info, /* RTP sock info */
&local_sdp); /* the SDP result */
PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
@@ -440,6 +550,51 @@ int main(int argc, char *argv[])
/* On exit, dump current memory usage: */
dump_pool_usage(THIS_FILE, &cp);
+ /* Destroy audio ports. Destroy the audio port first
+ * before the stream since the audio port has threads
+ * that get/put frames to the stream.
+ */
+ if (g_snd_port)
+ pjmedia_snd_port_destroy(g_snd_port);
+
+ /* Destroy video ports */
+#if PJMEDIA_HAS_VIDEO
+ if (g_vid_capturer)
+ pjmedia_vid_port_destroy(g_vid_capturer);
+ if (g_vid_renderer)
+ pjmedia_vid_port_destroy(g_vid_renderer);
+#endif
+
+ /* Destroy streams */
+ if (g_med_stream)
+ pjmedia_stream_destroy(g_med_stream);
+#if PJMEDIA_HAS_VIDEO
+ if (g_med_vstream)
+ pjmedia_vid_stream_destroy(g_med_vstream);
+#endif
+
+ /* Destroy media transports */
+ for (i = 0; i < MAX_MEDIA_CNT; ++i) {
+ if (g_med_transport[i])
+ pjmedia_transport_close(g_med_transport[i]);
+ }
+
+ /* Deinit ffmpeg codec */
+#if PJMEDIA_HAS_FFMPEG_CODEC
+ pjmedia_codec_ffmpeg_deinit();
+#endif
+
+ /* Deinit pjmedia endpoint */
+ if (g_med_endpt)
+ pjmedia_endpt_destroy(g_med_endpt);
+
+ /* Deinit pjsip endpoint */
+ if (g_endpt)
+ pjsip_endpt_destroy(g_endpt);
+
+ /* Release pool */
+ pj_pool_release(pool);
+
return 0;
}
@@ -574,9 +729,8 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
* Get media capability from media endpoint:
*/
- status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool, 1,
- &g_med_tpinfo.sock_info,
- &local_sdp);
+ status = pjmedia_endpt_create_sdp( g_med_endpt, rdata->tp_info.pool,
+ MAX_MEDIA_CNT, g_sock_info, &local_sdp);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, PJ_TRUE);
@@ -639,7 +793,7 @@ static pj_bool_t on_rx_request( pjsip_rx_data *rdata )
static void call_on_media_update( pjsip_inv_session *inv,
pj_status_t status)
{
- pjmedia_session_info sess_info;
+ pjmedia_stream_info stream_info;
const pjmedia_sdp_session *local_sdp;
const pjmedia_sdp_session *remote_sdp;
pjmedia_port *media_port;
@@ -662,88 +816,211 @@ static void call_on_media_update( pjsip_inv_session *inv,
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);
+ /* 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 media session", status);
+ app_perror(THIS_FILE,"Unable to create audio stream info",status);
return;
}
- /* If required, we can also change some settings in the session info,
+ /* If required, we can also change some settings in the stream info,
* (such as jitter buffer settings, codec settings, etc) before we
- * create the session.
+ * create the stream.
*/
- /* Create new media session, passing the two SDPs, and also the
+ /* Create new audio media stream, passing the stream info, 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 );
+ 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 media session", status);
+ 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 first stream in the session.
+ /* 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_session_get_port(g_med_session, 0, &media_port);
-
+ 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);
-
- /* 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);
+ app_perror( THIS_FILE, "Unable to create sound port", 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 */
+ 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_player, media_port);
+ status = pjmedia_snd_port_connect(g_snd_port, media_port);
- /* Create a sound recorder device and connect the media port to the
- * sound device.
+ /* 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.
*/
- 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;
- }
+#if PJMEDIA_HAS_VIDEO
+ 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;
+ }
- status = pjmedia_snd_port_connect(g_snd_rec, media_port);
+ /* 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. */
}