Implement libcap-based example listener
authorKatja Rohloff <katja.rohloff@uni-jena.de>
Thu, 13 Jun 2013 14:38:24 +0000 (16:38 +0200)
committerAdrian Knoth <adi@drcomp.erfurt.thur.de>
Thu, 13 Jun 2013 14:38:24 +0000 (16:38 +0200)
examples/simple_listener/Makefile [new file with mode: 0644]
examples/simple_listener/README [new file with mode: 0644]
examples/simple_listener/simple_listener.c [new file with mode: 0644]

diff --git a/examples/simple_listener/Makefile b/examples/simple_listener/Makefile
new file mode 100644 (file)
index 0000000..936c0e3
--- /dev/null
@@ -0,0 +1,8 @@
+CC = gcc\r
+LDLIBS = -std=gnu99 -lpcap -lsndfile\r
+\r
+simple_listener: simple_listener.c\r
+       $(CC) -o $@ $< $(LDLIBS) $(LDFLAGS)\r
+\r
+all: simple_listener\r
+\r
diff --git a/examples/simple_listener/README b/examples/simple_listener/README
new file mode 100644 (file)
index 0000000..d88c58e
--- /dev/null
@@ -0,0 +1,11 @@
+Simple libpcap-based AVB listener
+
+Dumps a stereo AVB audio stream to a wav file.
+
+Requirements:
+
+   * running mrpd
+   * libpcap
+
+
+Can be tested against simple_talker.
diff --git a/examples/simple_listener/simple_listener.c b/examples/simple_listener/simple_listener.c
new file mode 100644 (file)
index 0000000..959cf9a
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+Copyright (c) 2013 Katja Rohloff <Katja.Rohloff@uni-jena.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <pcap/pcap.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <sndfile.h>
+
+//#define DEBUG
+#define PCAP
+#define LIBSND
+
+#define MAX_MRPD_CMDSZ 1500
+#define MRPD_PORT_DEFAULT 7500
+
+#define ETHERNET_HEADER_SIZE 18
+#define SEVENTEEN22_HEADER_PART1_SIZE 4
+#define STREAM_ID_SIZE 8
+#define SEVENTEEN22_HEADER_PART2_SIZE 10
+#define SIX1883_HEADER_SIZE 10
+#define HEADER_SIZE ETHERNET_HEADER_SIZE + SEVENTEEN22_HEADER_PART1_SIZE + STREAM_ID_SIZE + SEVENTEEN22_HEADER_PART2_SIZE + SIX1883_HEADER_SIZE 
+
+#define SAMPLES_PER_SECOND 48000
+#define SAMPLES_PER_FRAME 6
+#define CHANNELS 2
+
+struct six1883_sample{
+       uint8_t label;
+       uint8_t value[3];
+};
+
+struct ethernet_header{
+       u_char dst[6];
+       u_char src[6];
+       u_char stuff[4];
+       u_char type[2];
+};
+
+typedef int (*process_msg) (char *buf, int buflen);
+
+// global
+unsigned char stream_id[8];
+volatile int talker = 0;
+int control_socket;
+pcap_t* handle;
+u_char ETHER_TYPE[] = { 0x22, 0xf0 };
+SNDFILE* snd_file;
+
+static void help()
+{
+       fprintf(stderr, "\n"
+               "Usage: listener [-h] -i interface -f file_name.wav"
+               "\n"
+               "Options:\n"
+               "    -h  show this message\n"
+               "    -i  specify interface for AVB connection\n"
+               "    -f  set the name of the output wav-file\n" 
+               "\n" "%s" "\n");
+       exit(1);
+}
+
+void pcap_callback(u_char* args, const struct pcap_pkthdr* packet_header, const u_char* packet)
+{
+       unsigned char* test_stream_id;
+       struct ethernet_header* eth_header;
+       struct six1883_sample* sample; 
+       uint32_t buf;
+       uint32_t *mybuf;
+       uint32_t frame[2] = { 0 , 0 };
+
+#ifdef DEBUG
+       fprintf(stdout,"Got packet.\n");
+#endif 
+
+       eth_header = (struct ethernet_header*)(packet);
+
+#ifdef DEBUG
+       fprintf(stdout,"Ether Type: 0x%02x%02x\n", eth_header->type[0], eth_header->type[1]);
+#endif
+
+       if (0 == memcmp(ETHER_TYPE,eth_header->type,sizeof(eth_header->type)))
+       {               
+               test_stream_id = (unsigned char*)(packet + ETHERNET_HEADER_SIZE + SEVENTEEN22_HEADER_PART1_SIZE);
+
+#ifdef DEBUG
+               fprintf(stderr, "Received stream id: %02x%02x%02x%02x%02x%02x%02x%02x\n ",
+                            test_stream_id[0], test_stream_id[1],
+                            test_stream_id[2], test_stream_id[3],
+                            test_stream_id[4], test_stream_id[5],
+                            test_stream_id[6], test_stream_id[7]);
+#endif
+
+               if (0 == memcmp(test_stream_id, stream_id, sizeof(STREAM_ID_SIZE)))
+               {
+
+#ifdef DEBUG
+                       fprintf(stdout,"Stream ids matched.\n");
+#endif
+
+                       //sample = (struct six1883_sample*) (packet + HEADER_SIZE);
+                       mybuf = (uint32_t*) (packet + HEADER_SIZE);
+                       for(int i = 0; i < SAMPLES_PER_FRAME * CHANNELS; i += 2)
+                       {       
+                               memcpy(&frame[0], &mybuf[i], sizeof(frame));
+
+                               frame[0] = ntohl(frame[0]);   /* convert to host-byte order */
+                               frame[1] = ntohl(frame[1]);
+                               frame[0] &= 0x00ffffff;       /* ignore leading label */
+                               frame[1] &= 0x00ffffff;
+                               frame[0] <<= 8;               /* left-align remaining PCM-24 sample */
+                               frame[1] <<= 8;
+
+                               sf_writef_int(snd_file, frame, 1);
+                       }
+               }       
+       }
+}
+
+int create_socket()
+{
+       struct sockaddr_in addr;
+       control_socket = socket(AF_INET, SOCK_DGRAM, 0);
+               
+       /** in POSIX fd 0,1,2 are reserved */
+       if (2 > control_socket)
+       {
+               if (-1 > control_socket)
+                       close(control_socket);
+       return -1;
+       }
+       
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(0);
+       
+       if(0 > (bind(control_socket, (struct sockaddr*)&addr, sizeof(addr)))) 
+       {
+               fprintf(stderr, "Could not bind socket.\n");
+               close(control_socket);
+               return -1;
+       }
+       return 0;
+}
+
+
+int msg_process(char *buf, int buflen)
+{
+       uint32_t id;
+       fprintf(stderr, "Msg: %s\n", buf);
+       int l = 0;
+       if ('S' == buf[l++] && 'N' == buf[l++] && 'E' == buf[l++] && 'T' == buf[++l])
+       {
+               while ('S' != buf[l++]);
+               l++;
+               for(int j = 0; j < 8 ; l+=2, j++)
+               {
+                       sscanf(&buf[l],"%02x",&id);
+                       stream_id[j] = (unsigned char)id;
+               }
+               talker = 1;
+       }
+       return (0);
+}
+
+int recv_msg()
+{
+       char *databuf;
+       int bytes = 0;
+
+       databuf = (char *)malloc(2000);
+       if (NULL == databuf)
+               return -1;
+
+       memset(databuf, 0, 2000);
+       bytes = recv(control_socket, databuf, 2000, 0);
+       if (bytes <= -1) 
+       {
+               free(databuf);
+               return (-1);
+       }
+       return msg_process(databuf, bytes);
+
+}
+
+int await_talker()
+{
+       while (0 == talker)     
+               recv_msg();
+       return 0;
+}
+
+int send_msg(char *data, int data_len)
+{
+       struct sockaddr_in addr;
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(MRPD_PORT_DEFAULT);
+       addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+       inet_aton("127.0.0.1", &addr.sin_addr);
+       if (-1 != control_socket)
+               return (sendto(control_socket, data, data_len, 0, (struct sockaddr*)&addr, (socklen_t)sizeof(addr)));
+       else 
+               return 0;
+}
+
+int mrp_disconnect()
+{
+       int rc;
+       char *msgbuf = malloc(1500);
+       if (NULL == msgbuf)
+               return -1;
+       memset(msgbuf, 0, 1500);
+       sprintf(msgbuf, "BYE");
+       rc = send_msg(msgbuf, 1500);
+
+       free(msgbuf);
+       return rc;
+}
+       
+int report_domain_status()
+{
+       int rc;
+       char* msgbuf = malloc(1500);
+
+       if (NULL == msgbuf)
+               return -1;
+       memset(msgbuf, 0, 1500);
+       sprintf(msgbuf, "S+D:C=6,P=3,V=0002");
+       
+       rc = send_msg(msgbuf, 1500);
+
+       free(msgbuf);
+       return rc;
+}
+
+int send_ready()
+{
+       char *databuf;
+       int rc;
+       databuf = malloc(1500);
+       if (NULL == databuf)
+               return -1;
+       memset(databuf, 0, 1500);
+       sprintf(databuf, "S+L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=2",
+                    stream_id[0], stream_id[1],
+                    stream_id[2], stream_id[3],
+                    stream_id[4], stream_id[5],
+                    stream_id[6], stream_id[7]);
+       rc = send_msg(databuf, 1500);
+
+#ifdef DEBUG
+       fprintf(stdout,"Ready-Msg: %s\n", databuf);
+#endif 
+
+       free(databuf);
+       return rc;
+}
+
+int send_leave()
+{
+       char *databuf;
+       int rc;
+       databuf = malloc(1500);
+       if (NULL == databuf)
+               return -1;
+       memset(databuf, 0, 1500);
+       sprintf(databuf, "S-L:L=%02x%02x%02x%02x%02x%02x%02x%02x, D=3",
+                    stream_id[0], stream_id[1],
+                    stream_id[2], stream_id[3],
+                    stream_id[4], stream_id[5],
+                    stream_id[6], stream_id[7]);
+       rc = send_msg(databuf, 1500);
+       free(databuf);
+       return rc;
+}
+
+void sigint_handler(int signum)
+{
+       fprintf(stdout,"Leaving...\n");
+       
+       if (0 != talker)
+               send_leave();
+
+       if (2 > control_socket)
+       {
+               close(control_socket);
+               mrp_disconnect();
+       }
+
+#ifdef PCAP
+       if (NULL != handle) 
+       {
+               pcap_breakloop(handle);
+               pcap_close(handle);
+       }
+#endif
+       
+#ifdef LIBSND
+       sf_write_sync(snd_file);
+       sf_close(snd_file);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+       char* file_name = NULL;
+       char* dev = NULL;
+       char errbuf[PCAP_ERRBUF_SIZE];
+       struct bpf_program comp_filter_exp;             /* The compiled filter expression */
+       char filter_exp[] = "ether dst 91:E0:F0:00:0e:80";      /* The filter expression */
+       struct pcap_pkthdr header;      /* header pcap gives us */
+       const u_char* packet;           /* actual packet */
+
+       signal(SIGINT, sigint_handler);
+
+       int c;
+       while((c = getopt(argc, argv, "hi:f:")) > 0) 
+       {
+               switch (c) 
+               {
+               case 'h': 
+                       help();
+                       break;
+               case 'i':
+                       dev = strdup(optarg);
+                       break;
+               case 'f':
+                       file_name = strdup(optarg);
+                       break;
+               default:
+                       fprintf(stderr, "Unrecognized option!\n");
+               }
+       }
+
+       if ((NULL == dev) || (NULL == file_name))
+               help();
+
+       if (create_socket())
+       {
+               fprintf(stderr, "Socket creation failed.\n");
+               return (errno);
+       }
+
+       report_domain_status();
+
+       fprintf(stdout,"Waiting for talker...\n");
+       await_talker(); 
+
+#ifdef DEBUG
+       fprintf(stdout,"Send ready-msg...\n");
+#endif
+       send_ready();
+               
+#ifdef LIBSND
+       SF_INFO* sf_info = (SF_INFO*)malloc(sizeof(SF_INFO));
+
+       memset(sf_info, 0, sizeof(SF_INFO));
+       
+       sf_info->samplerate = SAMPLES_PER_SECOND;
+       sf_info->channels = CHANNELS;
+       sf_info->format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
+
+       if (0 == sf_format_check(sf_info))
+       {
+               fprintf(stderr, "Wrong format.");
+               return -1;
+       }
+                       
+       if (NULL == (snd_file = sf_open(file_name, SFM_WRITE, sf_info)))
+       {
+               fprintf(stderr, "Could not create file.");
+               return -1;
+       }
+       fprintf(stdout,"Created file called %s\n", file_name);  
+#endif
+
+#ifdef PCAP            
+       /** session, get session handler */
+       /* take promiscuous vs. non-promiscuous sniffing? (0 or 1) */
+       handle = pcap_open_live(dev, BUFSIZ, 1, -1, errbuf);
+       if (NULL == handle) 
+       {
+               fprintf(stderr, "Could not open device %s: %s\n", dev, errbuf);
+               return -1;
+       }
+
+#ifdef DEBUG
+       fprintf(stdout,"Got session handler.\n");
+#endif
+       /* compile and apply filter */
+       if (-1 == pcap_compile(handle, &comp_filter_exp, filter_exp, 0, PCAP_NETMASK_UNKNOWN))
+       {
+               fprintf(stderr, "Could not parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
+               return -1;
+       }
+
+       if (-1 == pcap_setfilter(handle, &comp_filter_exp)) 
+       {
+               fprintf(stderr, "Could not install filter %s: %s\n", filter_exp, pcap_geterr(handle));
+               return -1;
+       }
+
+#ifdef DEBUG
+       fprintf(stdout,"Compiled and applied filter.\n");
+#endif
+
+       /** loop forever and call callback-function for every received packet */
+       pcap_loop(handle, -1, pcap_callback, NULL);
+#endif
+
+       return 0;
+}