summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-02-02 09:12:13 +0000
committerBenny Prijono <bennylp@teluu.com>2008-02-02 09:12:13 +0000
commit18993521e89c2157c0be2f10b038f2600e54eaa9 (patch)
treee9aa88dae4c3b1a2bf86d70cc1cb7b9fffc14e8e
parent289a68d8d6b3df5ad39d4280f88822be4d91c58e (diff)
More ticket #465: option to return UDP header in pj_pcap_read_udp(), and better RTP packet handling in pcaputil sample
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1769 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjlib-util/include/pjlib-util/pcap.h25
-rw-r--r--pjlib-util/src/pjlib-util/pcap.c19
-rw-r--r--pjsip-apps/src/samples/pcaputil.c206
3 files changed, 177 insertions, 73 deletions
diff --git a/pjlib-util/include/pjlib-util/pcap.h b/pjlib-util/include/pjlib-util/pcap.h
index e27f5709..58d39a9f 100644
--- a/pjlib-util/include/pjlib-util/pcap.h
+++ b/pjlib-util/include/pjlib-util/pcap.h
@@ -60,9 +60,22 @@ typedef enum pj_pcap_proto_type
/**
+ * This describes UDP header, which may optionally be returned in
+ * #pj_pcap_read_udp() function. All fields are in network byte order.
+ */
+typedef struct pj_pcap_udp_hdr
+{
+ pj_uint16_t src_port; /**< Source port. */
+ pj_uint16_t dst_port; /**< Destination port */
+ pj_uint16_t len; /**< Length. */
+ pj_uint16_t csum; /**< Checksum. */
+} pj_pcap_udp_hdr;
+
+
+/**
* This structure describes the filter to be used when reading packets from
- * a PCAP file. The filter is used to select what packets can be returned
- * to a read operation.
+ * a PCAP file. When a filter is configured, only packets matching all the
+ * filter specifications will be read from PCAP file.
*/
typedef struct pj_pcap_filter
{
@@ -142,7 +155,8 @@ PJ_DECL(pj_status_t) pj_pcap_open(pj_pool_t *pool,
PJ_DECL(pj_status_t) pj_pcap_close(pj_pcap_file *file);
/**
- * Configure filter for reading the file.
+ * Configure filter for reading the file. When filter is configured,
+ * only packets matching all the filter settings will be returned.
*
* @param file PCAP file handle.
* @param filter The filter.
@@ -153,9 +167,11 @@ PJ_DECL(pj_status_t) pj_pcap_set_filter(pj_pcap_file *file,
const pj_pcap_filter *filter);
/**
- * Read UDP payload from the next packet in the PCAP file.
+ * Read UDP payload from the next packet in the PCAP file. Optionally it
+ * can return the UDP header, if caller supplies it.
*
* @param file PCAP file handle.
+ * @param udp_hdr Optional buffer to receive UDP header.
* @param udp_payload Buffer to receive the UDP payload.
* @param udp_payload_size On input, specify the size of the buffer.
* On output, it will be filled with the actual size
@@ -164,6 +180,7 @@ PJ_DECL(pj_status_t) pj_pcap_set_filter(pj_pcap_file *file,
* @return PJ_SUCCESS on success, or the appropriate error code.
*/
PJ_DECL(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file,
+ pj_pcap_udp_hdr *udp_hdr,
pj_uint8_t *udp_payload,
pj_size_t *udp_payload_size);
diff --git a/pjlib-util/src/pjlib-util/pcap.c b/pjlib-util/src/pjlib-util/pcap.c
index 22d602d5..40ee7b3f 100644
--- a/pjlib-util/src/pjlib-util/pcap.c
+++ b/pjlib-util/src/pjlib-util/pcap.c
@@ -54,11 +54,12 @@ typedef struct pj_pcap_rec_hdr
} pj_pcap_rec_hdr;
#if 0
+/* gcc insisted on aligning this struct to 32bit on ARM */
typedef struct pj_pcap_eth_hdr
{
pj_uint8_t dest[6];
pj_uint8_t src[6];
- pj_uint8_t len[2]; /* problem with struct size if pj_uint16_t */
+ pj_uint8_t len[2];
} pj_pcap_eth_hdr;
#else
typedef pj_uint8_t pj_pcap_eth_hdr[14];
@@ -78,16 +79,6 @@ typedef struct pj_pcap_ip_hdr
pj_uint32_t ip_dst;
} pj_pcap_ip_hdr;
-typedef struct pj_pcap_udp_hdr
-{
- pj_uint16_t src_port;
- pj_uint16_t dst_port;
- pj_uint16_t len;
- pj_uint16_t csum;
-} pj_pcap_udp_hdr;
-
-#pragma pack()
-
/* Implementation of pcap file */
struct pj_pcap_file
{
@@ -210,6 +201,7 @@ static pj_status_t skip(pj_oshandle_t fd, pj_off_t bytes)
/* Read UDP packet */
PJ_DEF(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file,
+ pj_pcap_udp_hdr *udp_hdr,
pj_uint8_t *udp_payload,
pj_size_t *udp_payload_size)
{
@@ -342,6 +334,11 @@ PJ_DEF(pj_status_t) pj_pcap_read_udp(pj_pcap_file *file,
continue;
}
+ /* Copy UDP header if caller wants it */
+ if (udp_hdr) {
+ pj_memcpy(udp_hdr, &tmp.udp, sizeof(*udp_hdr));
+ }
+
/* Calculate payload size */
sz = pj_ntohs(tmp.udp.len) - sizeof(tmp.udp);
break;
diff --git a/pjsip-apps/src/samples/pcaputil.c b/pjsip-apps/src/samples/pcaputil.c
index f2d33111..e5954105 100644
--- a/pjsip-apps/src/samples/pcaputil.c
+++ b/pjsip-apps/src/samples/pcaputil.c
@@ -57,12 +57,19 @@ static const char *USAGE =
"Remote streaming is not supported yet."
;
-static pj_caching_pool cp;
-static pj_pool_t *pool;
-static pjmedia_endpt *mept;
-static pj_pcap_file *pcap;
-static pjmedia_port *wav;
-static pjmedia_transport *srtp;
+static struct app
+{
+ pj_caching_pool cp;
+ pj_pool_t *pool;
+ pjmedia_endpt *mept;
+ pj_pcap_file *pcap;
+ pjmedia_port *wav;
+ pjmedia_codec *codec;
+ unsigned pt;
+ pjmedia_transport *srtp;
+ pjmedia_rtp_session rtp_sess;
+ pj_bool_t rtp_sess_init;
+} app;
static void err_exit(const char *title, pj_status_t status)
{
@@ -74,12 +81,29 @@ static void err_exit(const char *title, pj_status_t status)
printf("Error: %s\n", title);
}
- if (srtp) pjmedia_transport_close(srtp);
- if (wav) pjmedia_port_destroy(wav);
- if (pcap) pj_pcap_close(pcap);
- if (mept) pjmedia_endpt_destroy(mept);
- if (pool) pj_pool_release(pool);
- pj_caching_pool_destroy(&cp);
+ if (app.srtp) pjmedia_transport_close(app.srtp);
+ if (app.wav) {
+ pj_ssize_t pos = pjmedia_wav_writer_port_get_pos(app.wav);
+ if (pos >= 0) {
+ unsigned msec;
+ msec = pos / 2 * 1000 / app.wav->info.clock_rate;
+ printf("Written: %dm:%02ds.%03d\n",
+ msec / 1000 / 60,
+ (msec / 1000) % 60,
+ msec % 1000);
+ }
+ pjmedia_port_destroy(app.wav);
+ }
+ if (app.pcap) pj_pcap_close(app.pcap);
+ if (app.codec) {
+ pjmedia_codec_mgr *cmgr;
+ app.codec->op->close(app.codec);
+ cmgr = pjmedia_endpt_get_codec_mgr(app.mept);
+ pjmedia_codec_mgr_dealloc_codec(cmgr, app.codec);
+ }
+ if (app.mept) pjmedia_endpt_destroy(app.mept);
+ if (app.pool) pj_pool_release(app.pool);
+ pj_caching_pool_destroy(&app.cp);
pj_shutdown();
exit(1);
@@ -97,27 +121,91 @@ static void read_rtp(pj_uint8_t *buf, pj_size_t bufsize,
pj_uint8_t **payload,
unsigned *payload_size)
{
- pj_size_t sz = bufsize;
pj_status_t status;
- status = pj_pcap_read_udp(pcap, buf, &sz);
- if (status != PJ_SUCCESS)
- err_exit("Error reading PCAP file", status);
-
- if (sz < sizeof(pjmedia_rtp_hdr) + 10) {
- err_exit("Invalid RTP packet", PJ_SUCCESS);
+ /* Init RTP session */
+ if (!app.rtp_sess_init) {
+ T(pjmedia_rtp_session_init(&app.rtp_sess, 0, 0));
+ app.rtp_sess_init = PJ_TRUE;
}
- /* Decrypt SRTP */
- if (srtp) {
- int len = sz;
- T(pjmedia_transport_srtp_decrypt_pkt(srtp, PJ_TRUE, buf, &len));
- sz = len;
- }
+ /* Loop reading until we have a good RTP packet */
+ for (;;) {
+ pj_size_t sz = bufsize;
+ const pjmedia_rtp_hdr *r;
+ const void *p;
+ pjmedia_rtp_status seq_st;
+
+ status = pj_pcap_read_udp(app.pcap, NULL, buf, &sz);
+ if (status != PJ_SUCCESS)
+ err_exit("Error reading PCAP file", status);
+
+ /* Decode RTP packet to make sure that this is an RTP packet.
+ * We will decode it again to get the payload after we do
+ * SRTP decoding
+ */
+ status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, sz, &r,
+ &p, payload_size);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ printf("Not RTP packet, skipping packet: %s\n", errmsg);
+ continue;
+ }
+
+ /* Decrypt SRTP */
+ if (app.srtp) {
+ int len = sz;
+ status = pjmedia_transport_srtp_decrypt_pkt(app.srtp, PJ_TRUE,
+ buf, &len);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ printf("SRTP packet decryption failed, skipping packet: %s\n",
+ errmsg);
+ continue;
+ }
+ sz = len;
+
+ /* Decode RTP packet again */
+ status = pjmedia_rtp_decode_rtp(&app.rtp_sess, buf, sz, &r,
+ &p, payload_size);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ printf("Not RTP packet, skipping packet: %s\n", errmsg);
+ continue;
+ }
+ }
- *rtp = (pjmedia_rtp_hdr*)buf;
- *payload = (pj_uint8_t*) (buf + sizeof(pjmedia_rtp_hdr));
- *payload_size = sz - sizeof(pjmedia_rtp_hdr);
+ /* Update RTP session */
+ pjmedia_rtp_session_update(&app.rtp_sess, r, &seq_st);
+
+ /* Skip out-of-order packet */
+ if (seq_st.diff == 0) {
+ printf("Skipping out of order packet\n");
+ continue;
+ }
+
+ /* Skip if payload type is different */
+ if (r->pt != app.pt) {
+ printf("Skipping RTP packet with bad payload type\n");
+ continue;
+ }
+
+ /* Skip bad packet */
+ if (seq_st.status.flag.bad) {
+ printf("Skipping bad RTP\n");
+ continue;
+ }
+
+
+ *rtp = (pjmedia_rtp_hdr*)r;
+ *payload = (pj_uint8_t*)p;
+
+ /* We have good packet */
+ break;
+ }
}
static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
@@ -131,31 +219,30 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
unsigned payload_len;
} pkt0;
pjmedia_codec_mgr *cmgr;
- pjmedia_codec_info *ci;
+ const pjmedia_codec_info *ci;
pjmedia_codec_param param;
- pjmedia_codec *codec;
unsigned samples_per_frame;
pj_status_t status;
/* Initialize all codecs */
#if PJMEDIA_HAS_SPEEX_CODEC
- T( pjmedia_codec_speex_init(mept, 0, 10, 10) );
+ T( pjmedia_codec_speex_init(app.mept, 0, 10, 10) );
#endif /* PJMEDIA_HAS_SPEEX_CODEC */
#if PJMEDIA_HAS_ILBC_CODEC
- T( pjmedia_codec_ilbc_init(mept, 30) );
+ T( pjmedia_codec_ilbc_init(app.mept, 30) );
#endif /* PJMEDIA_HAS_ILBC_CODEC */
#if PJMEDIA_HAS_GSM_CODEC
- T( pjmedia_codec_gsm_init(mept) );
+ T( pjmedia_codec_gsm_init(app.mept) );
#endif /* PJMEDIA_HAS_GSM_CODEC */
#if PJMEDIA_HAS_G711_CODEC
- T( pjmedia_codec_g711_init(mept) );
+ T( pjmedia_codec_g711_init(app.mept) );
#endif /* PJMEDIA_HAS_G711_CODEC */
#if PJMEDIA_HAS_L16_CODEC
- T( pjmedia_codec_l16_init(mept, 0) );
+ T( pjmedia_codec_l16_init(app.mept, 0) );
#endif /* PJMEDIA_HAS_L16_CODEC */
/* Create SRTP transport is needed */
@@ -165,32 +252,33 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
pj_bzero(&crypto, sizeof(crypto));
crypto.key = *srtp_key;
crypto.name = *srtp_crypto;
- T( pjmedia_transport_srtp_create(mept, NULL, NULL, &srtp) );
- T( pjmedia_transport_srtp_start(srtp, &crypto, &crypto) );
+ T( pjmedia_transport_srtp_create(app.mept, NULL, NULL, &app.srtp) );
+ T( pjmedia_transport_srtp_start(app.srtp, &crypto, &crypto) );
}
/* Read first packet */
read_rtp(pkt0.buffer, sizeof(pkt0.buffer), &pkt0.rtp,
&pkt0.payload, &pkt0.payload_len);
- cmgr = pjmedia_endpt_get_codec_mgr(mept);
+ cmgr = pjmedia_endpt_get_codec_mgr(app.mept);
/* Get codec info and param for the specified payload type */
+ app.pt = pkt0.rtp->pt;
T( pjmedia_codec_mgr_get_codec_info(cmgr, pkt0.rtp->pt, &ci) );
T( pjmedia_codec_mgr_get_default_param(cmgr, ci, &param) );
/* Alloc and init codec */
- T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &codec) );
- T( codec->op->init(codec, pool) );
- T( codec->op->open(codec, &param) );
+ T( pjmedia_codec_mgr_alloc_codec(cmgr, ci, &app.codec) );
+ T( app.codec->op->init(app.codec, app.pool) );
+ T( app.codec->op->open(app.codec, &param) );
/* Open WAV file */
samples_per_frame = ci->clock_rate * param.info.frm_ptime / 1000;
- T( pjmedia_wav_writer_port_create(pool, wav_filename,
+ T( pjmedia_wav_writer_port_create(app.pool, wav_filename,
ci->clock_rate, ci->channel_cnt,
samples_per_frame,
param.info.pcm_bits_per_sample, 0, 0,
- &wav) );
+ &app.wav) );
/* Loop reading PCAP and writing WAV file */
for (;;) {
@@ -206,8 +294,8 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
/* Parse first packet */
ts.u64 = 0;
frame_cnt = PJ_ARRAY_SIZE(frames);
- T( codec->op->parse(codec, pkt0.payload, pkt0.payload_len, &ts,
- &frame_cnt, frames) );
+ T( app.codec->op->parse(app.codec, pkt0.payload, pkt0.payload_len,
+ &ts, &frame_cnt, frames) );
/* Decode and write to WAV file */
samples_cnt = 0;
@@ -217,8 +305,9 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
pcm_frame.buf = pcm;
pcm_frame.size = samples_per_frame * 2;
- T( codec->op->decode(codec, &frames[i], pcm_frame.size, &pcm_frame) );
- T( pjmedia_port_put_frame(wav, &pcm_frame) );
+ T( app.codec->op->decode(app.codec, &frames[i], pcm_frame.size,
+ &pcm_frame) );
+ T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
samples_cnt += samples_per_frame;
}
@@ -234,13 +323,14 @@ static void pcap2wav(const char *wav_filename, const pj_str_t *srtp_crypto,
pcm_frame.buf = pcm;
pcm_frame.size = samples_per_frame * 2;
- if (codec->op->recover) {
- T( codec->op->recover(codec, pcm_frame.size, &pcm_frame) );
+ if (app.codec->op->recover) {
+ T( app.codec->op->recover(app.codec, pcm_frame.size,
+ &pcm_frame) );
} else {
pj_bzero(pcm_frame.buf, pcm_frame.size);
}
- T( pjmedia_port_put_frame(wav, &pcm_frame) );
+ T( pjmedia_port_put_frame(app.wav, &pcm_frame) );
ts_gap -= samples_per_frame;
}
@@ -340,14 +430,14 @@ int main(int argc, char *argv[])
T( pj_init() );
- pj_caching_pool_init(&cp, NULL, 0);
- pool = pj_pool_create(&cp.factory, "pcaputil", 1000, 1000, NULL);
+ pj_caching_pool_init(&app.cp, NULL, 0);
+ app.pool = pj_pool_create(&app.cp.factory, "pcaputil", 1000, 1000, NULL);
T( pjlib_util_init() );
- T( pjmedia_endpt_create(&cp.factory, NULL, 0, &mept) );
+ T( pjmedia_endpt_create(&app.cp.factory, NULL, 0, &app.mept) );
- T( pj_pcap_open(pool, input.ptr, &pcap) );
- T( pj_pcap_set_filter(pcap, &filter) );
+ T( pj_pcap_open(app.pool, input.ptr, &app.pcap) );
+ T( pj_pcap_set_filter(app.pcap, &filter) );
if (pj_stristr(&output, &wav)) {
pcap2wav(output.ptr, &srtp_crypto, &srtp_key);
@@ -355,9 +445,9 @@ int main(int argc, char *argv[])
err_exit("invalid output file", PJ_EINVAL);
}
- pjmedia_endpt_destroy(mept);
- pj_pool_release(pool);
- pj_caching_pool_destroy(&cp);
+ pjmedia_endpt_destroy(app.mept);
+ pj_pool_release(app.pool);
+ pj_caching_pool_destroy(&app.cp);
pj_shutdown();
return 0;
}