ref: e24c02237fb131393bd04f0d89ab63895371442c
parent: c9d0ef50a1283d2a3be9c4572c35884e31308713
author: Ralph Giles <[email protected]>
date: Fri Aug 10 13:16:07 EDT 2012
Hacky tool to sniff rtp traffic and write it to a file. I wrote this for debugging, but hopefully it can grow into a general opus rtp example, without pcap.
--- /dev/null
+++ b/src/opusrtp.c
@@ -1,0 +1,333 @@
+/* dump opus rtp packets into an ogg file
+ *
+ * compile with: gcc -g -Wall -o opusrtc opusrtp.c -lpcap -logg
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pcap.h>
+#include <ogg/ogg.h>
+
+/* state struct for passing around our handles */
+typedef struct {
+ ogg_stream_state *stream;
+ FILE *out;
+ int seq;
+} state;
+
+/* helper, write a little-endian 32 bit int to memory */
+void le32(unsigned char *p, int v)
+{
+ p[0] = v & 0xff;
+ p[1] = (v >> 8) & 0xff;
+ p[2] = (v >> 16) & 0xff;
+ p[3] = (v >> 24) & 0xff;
+}
+
+
+/* helper, write a little-endian 16 bit int to memory */
+void le16(unsigned char *p, int v)
+{
+ p[0] = v & 0xff;
+ p[1] = (v >> 8) & 0xff;
+}
+
+/* manufacture a generic OpusHead packet */
+ogg_packet *op_opushead()
+{
+ int size = 19;
+ unsigned char *data = malloc(size);
+ ogg_packet *op = malloc(sizeof(*op));
+
+ if (!data) {
+ fprintf(stderr, "Couldn't allocate data buffer.\n");
+ return NULL;
+ }
+ if (!op) {
+ fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+ return NULL;
+ }
+
+ memcpy(data, "OpusHead", 8); /* identifier */
+ data[8] = 1; /* version */
+ data[9] = 2; /* channels */
+ le16(data+10, 0); /* pre-skip */
+ le32(data + 12, 48000); /* original sample rate */
+ le16(data + 16, 0); /* gain */
+ data[18] = 0; /* channel mapping family */
+
+ op->packet = data;
+ op->bytes = size;
+ op->b_o_s = 1;
+ op->e_o_s = 0;
+ op->granulepos = 0;
+ op->packetno = 0;
+
+ return op;
+}
+
+
+/* manufacture a generic OpusTags packet */
+ogg_packet *op_opustags()
+{
+ char *identifier = "OpusTags";
+ char *vendor = "opus rtp packet dump";
+ int size = strlen(identifier) + 4 + strlen(vendor) + 4;
+ unsigned char *data = malloc(size);
+ ogg_packet *op = malloc(sizeof(*op));
+
+ if (!data) {
+ fprintf(stderr, "Couldn't allocate data buffer.\n");
+ return NULL;
+ }
+ if (!op) {
+ fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+ return NULL;
+ }
+
+ memcpy(data, identifier, 8);
+ le32(data + 8, strlen(vendor));
+ memcpy(data + 12, vendor, strlen(vendor));
+ le32(data + 12 + strlen(vendor), 0);
+
+ op->packet = data;
+ op->bytes = size;
+ op->b_o_s = 0;
+ op->e_o_s = 0;
+ op->granulepos = 0;
+ op->packetno = 1;
+
+ return op;
+}
+
+ogg_packet *op_from_pkt(unsigned char *pkt, int len)
+{
+ ogg_packet *op = malloc(sizeof(*op));
+ if (!op) {
+ fprintf(stderr, "Couldn't allocate Ogg packet.\n");
+ return NULL;
+ }
+
+ op->packet = pkt;
+ op->bytes = len;
+ op->b_o_s = 0;
+ op->e_o_s = 1;
+
+ return op;
+}
+
+/* free a packet and its contents */
+void op_free(ogg_packet *op) {
+ if (op) {
+ if (op->packet) {
+ free(op->packet);
+ }
+ free(op);
+ }
+}
+
+/* helper, write out available ogg pages */
+int ogg_write(state *params)
+{
+ ogg_page page;
+
+ if (!params || !params->stream || !params->out) {
+ return -1;
+ }
+
+ while (ogg_stream_pageout(params->stream, &page)) {
+ fwrite(page.header, 1, page.header_len, params->out);
+ fwrite(page.body, 1, page.body_len, params->out);
+ }
+
+ return 0;
+}
+
+/* helper, flush remaining ogg data */
+int ogg_flush(state *params)
+{
+ ogg_page page;
+
+ if (!params || !params->stream || !params->out) {
+ return -1;
+ }
+
+ while (ogg_stream_flush(params->stream, &page)) {
+ fwrite(page.header, 1, page.header_len, params->out);
+ fwrite(page.body, 1, page.body_len, params->out);
+ }
+
+ return 0;
+}
+
+/* pcap 'got_packet' callback */
+void write_packet(u_char *args, const struct pcap_pkthdr *header,
+ const u_char *packet)
+{
+ state *params = (state *)args;
+ int ip_header_size;
+ int rtp_header_size;
+
+ fprintf(stderr, "Got %d byte packet (%d bytes captured)\n",
+ header->len, header->caplen);
+
+ /* eth header is always 14 bytes */
+ if (header->caplen < 14) {
+ fprintf(stderr, "Packet too short for eth\n");
+ return;
+ }
+ fprintf(stderr, " src mac %02x:%02x:%02x:%02x:%02x:%02x\n",
+ packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]);
+ fprintf(stderr, " dest mac %02x:%02x:%02x:%02x:%02x:%02x\n",
+ packet[6], packet[7], packet[8], packet[9], packet[10], packet[11]);
+ fprintf(stderr, " eth type 0x%04x\n", packet[12] << 8 | packet[13]);
+
+ /* ipv4 header */
+ if (header->caplen < 14 + 20) {
+ fprintf(stderr, "Packet too short for ipv4\n");
+ return;
+ }
+ fprintf(stderr, " IP version %d\n", (packet[14+0] >> 4) & 0x0f);
+ ip_header_size = (packet[14+0] & 0x0f) * 4;
+ fprintf(stderr, " header length %d\n", ip_header_size);
+ fprintf(stderr, " src addr %d.%d.%d.%d\n",
+ packet[14+12+0], packet[14+12+1], packet[14+12+2], packet[14+12+3]);
+ fprintf(stderr, " dest addr %d.%d.%d.%d\n",
+ packet[14+16+0], packet[14+16+1], packet[14+16+2], packet[14+16+3]);
+ fprintf(stderr, " protocol %d\n", packet[14+9]);
+ if (header->caplen < 14 + ip_header_size) {
+ fprintf(stderr, "Packet too short for ipv4 with options\n");
+ return;
+ }
+
+ if (header->caplen < 14 + ip_header_size + 8) {
+ fprintf(stderr, "Packet too short for udp\n");
+ return;
+ }
+ fprintf(stderr, " src port %d\n", packet[14+ip_header_size] << 8 |
+ packet[14+ip_header_size + 1]);
+ fprintf(stderr, " dest port %d\n", packet[14+ip_header_size + 2] << 8 |
+ packet[14+ip_header_size + 3]);
+ fprintf(stderr, " udp length %d\n", packet[14+ip_header_size + 4] << 8 |
+ packet[14+ip_header_size + 5]);
+
+ if (header->caplen < 14 + ip_header_size + 8 + 12) {
+ fprintf(stderr, "Packet too short for rtp\n");
+ return;
+ }
+ fprintf(stderr, " rtp version %d\n",
+ (packet[14+ip_header_size + 8 + 0] >> 6) & 3);
+ fprintf(stderr, " padding %d\n",
+ (packet[14+ip_header_size + 8 + 0] >> 5) & 1);
+ fprintf(stderr, " extension %d\n",
+ (packet[14+ip_header_size + 8 + 0] >> 4) & 1);
+ rtp_header_size = 12 + 4*((packet[14+ip_header_size + 8 + 0]) & 7);
+ fprintf(stderr, " CSRC count %d\n",
+ (packet[14+ip_header_size + 8 + 0]) & 7);
+ fprintf(stderr, " marker %d\n",
+ (packet[14+ip_header_size + 8 + 1] >> 7) & 1);
+ fprintf(stderr, " payload type %d\n",
+ (packet[14+ip_header_size + 8 + 1]) & 127);
+ int seq = packet[14+ip_header_size + 8 + 2] << 8 |
+ packet[14+ip_header_size + 8 + 3];
+ fprintf(stderr, " sequence no %d\n", seq);
+
+ if (header->caplen < 14 + ip_header_size + 8 + rtp_header_size) {
+ fprintf(stderr, "skipping short packet\n");
+ return;
+ }
+
+ if (seq < params->seq) {
+ fprintf(stderr, "skipping out-of-sequence packet\n");
+ return;
+ }
+ params->seq = seq;
+
+ /* write the payload to our opus file */
+ int size = header->caplen - 14 - ip_header_size - 8 - rtp_header_size;
+ unsigned char *data = packet + 14 + ip_header_size + 8 + rtp_header_size;
+ fprintf(stderr, " payload data %d bytes\n",
+ header->len - 14 - ip_header_size - 8 - rtp_header_size);
+ if ( (packet[14+ip_header_size + 8 + 1] & 127) != 109) {
+ fprintf(stderr, "skipping non-opus packet\n");
+ return;
+ }
+ ogg_packet *op = op_from_pkt(data, size);
+ op->granulepos = 960*seq;
+ ogg_stream_packetin(params->stream, op);
+ free(op);
+ ogg_write(params);
+}
+
+int main(int argc, char *argv[])
+{
+ state *params;
+ pcap_t *pcap;
+ char *dev = "lo";
+ int port = 55555;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ ogg_packet *op;
+ int i;
+
+ /* set up */
+ pcap = pcap_open_live(dev, 9600, 0, 1000, errbuf);
+ if (pcap == NULL) {
+ fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
+ return(2);
+ }
+ params = malloc(sizeof(state));
+ if (!params) {
+ fprintf(stderr, "Couldn't allocate param struct.\n");
+ return -1;
+ }
+ params->stream = malloc(sizeof(ogg_stream_state));
+ if (!params->stream) {
+ fprintf(stderr, "Couldn't allocate stream struct.\n");
+ return -1;
+ }
+ if (ogg_stream_init(params->stream, rand()) < 0) {
+ fprintf(stderr, "Couldn't initialize Ogg stream state.\n");
+ return -1;
+ }
+ params->out = fopen("rtpdump.opus", "wb");
+ if (!params->out) {
+ fprintf(stderr, "Couldn't open output file.\n");
+ return -2;
+ }
+ params->seq = 0;
+
+ /* write stream headers */
+ op = op_opushead();
+ ogg_stream_packetin(params->stream, op);
+ op_free(op);
+ op = op_opustags();
+ ogg_stream_packetin(params->stream, op);
+ op_free(op);
+ ogg_flush(params);
+
+
+ /* start capture loop */
+ fprintf(stderr, "Capturing packets\n");
+ pcap_loop(pcap, 300, write_packet, (u_char *)params);
+
+ /* write packets */
+ int num_packets = 0;
+ unsigned char **packets;
+ int *sizes;
+ for (i = 0; i < num_packets; i++) {
+ }
+
+ /* write outstanding data */
+ ogg_flush(params);
+
+ /* clean up */
+ fclose(params->out);
+ ogg_stream_destroy(params->stream);
+ free(params);
+ pcap_close(pcap);
+
+ return 0;
+}