// Update official timestamp, own timestamp and sequence number in the RTP header. // The actual RTP message is stored in tx.udp_payload. int update_RTP(libnet_t *l, libnet_ptag_t t) { u_int8_t *ptr; struct mz_timestamp ts; tx.rtp_sqnr++; tx.rtp_stmp+=160; // TODO: different values for different codecs // update SQNR ptr = (u_int8_t*) &tx.rtp_sqnr; tx.udp_payload[2] = *(ptr+1); tx.udp_payload[3] = *ptr; // update official timestamp ptr = (u_int8_t*) &tx.rtp_stmp; tx.udp_payload[4] = *(ptr+3); tx.udp_payload[5] = *(ptr+2); tx.udp_payload[6] = *(ptr+1); tx.udp_payload[7] = *ptr; // update own timestamp getcurtime(&ts); // Now add TX timestamp: mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); t = libnet_build_udp(tx.sp, tx.dp, tx.udp_len, tx.udp_sum, tx.udp_payload, tx.udp_payload_s, l, t); if (t == -1) { fprintf(stderr," mz/send_frame: RTP header update failed!\n"); exit (1); } return 0; }
int create_rtp_packet() { u_int8_t byte1, byte2; u_int16_t seqnr; u_int8_t ssrc[4] = {0,0,0,0} ; int ssrc_s = 0; u_int8_t *ptr; char argval[MAX_PAYLOAD_SIZE]; unsigned int rtp_payload_size=160; struct mz_timestamp ts; if ( (getarg(tx.arg_string,"help", NULL)==1) && (mode==RTP) ) { if (mz_port) { cli_print(gcli, "%s", MZ_RTP_HELP); return -1; } else { fprintf(stderr,"\n" MAUSEZAHN_VERSION "\n%s", MZ_RTP_HELP); exit(0); } } if (getarg(tx.arg_string,"pld", argval)==1) { rtp_payload_size = (unsigned int) str2int(argval); } if (getarg(tx.arg_string,"codec", argval)==1) { tx.delay = 20000; } if (getarg(tx.arg_string,"ssrc", argval)==1) { ssrc_s = str2hex(argval, ssrc, 4); if (ssrc_s<0) { fprintf(stderr, " mz/rtp: invalid ssrc!\n"); return -1; } } // TODO: Optional arguments for RTP // Create header: // // Byte 1 // // +--+--+--+--+--+--+--+--+ // | ver | P| X| CSRC Count| // +--+--+--+--+--+--+--+--+ // // Default: ver=2, Padding=0, Extension_Header=1, CSRC_Count=0 => 10 0 1 0000 = 0x90 byte1 = 0x90; // Byte 2 // // +--+--+--+--+--+--+--+--+ // | M| Payload Type | // +--+--+--+--+--+--+--+--+ // // Marker=0, Payload Type=0 (or 8 alternatively) byte2 = 0x00; // Bytes 3,4 // // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ // | Sequence Number | // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ seqnr = 0x0000; // Bytes 5,6,7,8 // // Timestamp /* done below */ // // Bytes 9,10,11,12 // // Synchronization Source Identifier // if (ssrc_s==0) str2hex("ca:fe:fe:ed", ssrc, 4); // Bytes 13,14,15,16 // // CSRC - Contributing Source Identifiers (optional, only used by mixers) // // csrc = 0x00000000; // Bytes 17,18,19,20 // // Header Extension (optional) NOT USED HERE! // // !!! Thus payload begins with index 16 in a C array !!! // ------------ Now combine all fields: ---------------- tx.udp_payload[0] = byte1; tx.udp_payload[1] = byte2; ptr = (u_int8_t*) &seqnr; tx.udp_payload[2] = *(ptr+1); tx.udp_payload[3] = *ptr; // TIMESTAMP: will be linearly increased, e.g. using 20msec G.711: 0, 160, 320, ... tx.udp_payload[4] = 0x00; tx.udp_payload[5] = 0x00; tx.udp_payload[6] = 0x00; tx.udp_payload[7] = 0x00; tx.udp_payload[8] = ssrc[0]; tx.udp_payload[9] = ssrc[1]; tx.udp_payload[10] = ssrc[2]; tx.udp_payload[11] = ssrc[3]; /* ptr = (u_int8_t*) &csrc; tx.udp_payload[12] = *(ptr+3); tx.udp_payload[13] = *(ptr+2); tx.udp_payload[14] = *(ptr+1); tx.udp_payload[15] = *ptr; */ // Add the NEW Mausezahn extension header (see mops_ext_rtp.c) tx.udp_payload[12] = 0xca; // identifier tx.udp_payload[13] = 0xca; tx.udp_payload[14] = 0x00; tx.udp_payload[15] = 0x04; // length getcurtime(&ts); // Now add TX timestamp: mops_hton4 ((u_int32_t*) &ts.sec, &tx.udp_payload[16]); mops_hton4 ((u_int32_t*) &ts.nsec, &tx.udp_payload[20]); // NOTE: The remaining 8 bytes of this extension header are set to zero // via the following code. memset(&tx.udp_payload[24], 0x00, (rtp_payload_size-12)); // payload (considering our 8 byte timestamp) tx.udp_payload_s = 12 + rtp_payload_size; // the latter ist the payload size // ---------- now hand over to UDP ----------------- tx.dp = 30000; tx.sp = 30000; tx.udp_len = 8 + tx.udp_payload_s; return 0; }
// Handler function to do something when RTP messages are received void got_rtp_packet(u_char *args, const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') const u_char *packet) // the bytestring sniffed { const struct struct_ethernet *ethernet; const struct struct_ip *ip; const struct struct_udp *udp; const struct struct_rtp *rtp; int size_ethernet = sizeof(struct struct_ethernet); int size_ip = sizeof(struct struct_ip); int size_udp = sizeof(struct struct_udp); // int size_rtp = sizeof(struct struct_rtp); // ethernet = (struct struct_ethernet*)(packet); ip = (struct struct_ip*)(packet+size_ethernet); udp = (struct struct_udp*)(packet+size_ethernet+size_ip); rtp = (struct struct_rtp*)(packet+size_ethernet+size_ip+size_udp); struct mz_timestamp deltaTX, deltaRX; u_int32_t i, jitter_abs, jitter_avg, jitter_max, jitter_min, curtime=0; int32_t ltemp; u_int8_t *x,*y; char dummy[256]; char ts_hms[10]; unsigned char *dum; static u_int32_t drop_last=0, drop_prev=0; int s1, s2; // check if the RTP packet is really from a Mausezahn instance: if (compare4B((u_int8_t*) &rtp->ssrc, mz_ssrc)==0) { // we got a valid RTP packet from a Mausezahn instance // Get current SQNR and store it in 'sqnr_cur' in host byte order x = (u_int8_t*) &rtp->sqnr; y = (u_int8_t*) &sqnr_cur; *y = *(x+1); y++; *y = *x; ///////////////////////////////////////////////////////////////////// // Packet drop and disorder detection: if (sqnr0_flag) { if (sqnr_next==sqnr_cur) { // correct SQNR received sqnr_next++; sqnr_last++; } else if (sqnr_last>sqnr_cur) { // disordered sequence dis++; if (drop) drop--; // don't get below 0 else { // drop reached zero: resync (restarted RTP stream?) sqnr_last = sqnr_cur; sqnr_next = (++sqnr_last); dis=0; } } else { // packet drop drop += (sqnr_cur-sqnr_next); sqnr_last = sqnr_cur; sqnr_next = (++sqnr_last); } } else { // initial synchronization with observed SQNR: sqnr_last = sqnr_cur; sqnr_next = (++sqnr_last); sqnr0_flag++; } // ///////////////////////////////////////////////////////////////////// // Get RX timestamp from pcap header timeRX[gind].sec = header->ts.tv_sec; timeRX[gind].nsec = header->ts.tv_usec *1000; // Get TX timestamp from the packet mops_hton4((u_int32_t*) &rtp->time_sec, (u_int8_t*) &timeTX[gind].sec); mops_hton4((u_int32_t*) &rtp->time_nsec, (u_int8_t*) &timeTX[gind].nsec); // printf("%li %li\n", (long int) timeTX[gind].sec, (long int) timeTX[gind].nsec); gind++; //////////////////////////////////////////////////////////////// if (gind == gind_max) { // array full, now calculate statistics gind=0; gtotal++; jitter_avg = 0; jitter_min = 0xffffffff; jitter_max = 0; /////////////////////////////////////////////////////// // calculate deltas and jitters for (i=2; i<gind_max; i++) { // omit the first 2 data // entries because of // artificial high TX-delta! // /////////////////////////////////////////////// // calculate deltaTX and deltaRX // s1=timestamp_subtract (&timeTX[i], &timeTX[i-1], &deltaTX); s2=timestamp_subtract (&timeRX[i], &timeRX[i-1], &deltaRX); if (s1) fprintf(stderr, " *** ***\n"); // Then calculate the precise jitter by considering // also TX-jitter: (pseudo)jitter = deltaRX - deltaTX, // hence we have positive and negative jitter (delay // deviations) jitter entries are in +/- nanoseconds jitter[i] = (deltaRX.sec*1000000000L + deltaRX.nsec) - (deltaTX.sec*1000000000L + deltaTX.nsec); // Calculate RFC 3550 jitter estimation. According to // that RFC the jitter should be measured in timestamp // units; however currently Mausezahn uses nanoseconds. // (If we want to solve this: G.711 timestamp units are // 125 usec, so jitter/=125 would be sufficient, AFAIK) ltemp = labs(jitter[i]) - jitter_rfc; jitter_rfc += (ltemp>>4); // Add previous pseudojitter to get the true jitter // (See Documentation!) jitter[i] += jitter[i-1]; // //////////////////////////////////////////////// //////////////////////////////////////////////// // Determine avg, min, and max jitter within this time frame: jitter_abs = labs(jitter[i]); jitter_avg += jitter_abs; if (jitter_abs < jitter_min) jitter_min = jitter_abs; if (jitter_abs > jitter_max) jitter_max = jitter_abs; // //////////////////////////////// /// PRINT IN FILE_2: Detailed jitter data /// if (rtp_log==2) { // Calculate relative timestamp for column 1 of the datafile curtime = timeRX[i].sec*1000000+timeRX[i].nsec/1000; if (time0_flag) { curtime = curtime - time0; } else { // this is only done once during the Mausezahn process time0 = curtime; time0_flag=1; curtime = curtime - time0; } fprintf(fp2, "%lu, %li\n", (long unsigned int) curtime, (long int) jitter[i]); fflush(fp2); // save everything immediately // (CHECK if fsync() is additionally needed) } } // end for (i=2; i<gind_max; i++) // //////////////////////////////////////////////////////// jitter_avg = jitter_avg / (gind_max-2); // average true jitter, always positive if (drop>=drop_prev) { // because the total drop count may decrease(!) if disordered packets appear lately drop_last = drop - drop_prev; drop_prev=drop; } else drop_last=0; // PRINT ON CLI: statistics data switch (rtp_dm) { case TEXT: dum = (unsigned char*) &ip->src; fprintf(stdout, "Got %u packets from host %u.%u.%u.%u: %lu lost (%lu absolute lost, %lu out of order)\n" " Jitter_RFC (low pass filtered) = %li usec\n" " Samples jitter (min/avg/max) = %lu/%lu/%lu usec\n", gind_max, *(dum),*(dum+1),*(dum+2),*(dum+3), (long unsigned int) drop_last, (long unsigned int) drop, (long unsigned int) dis, (long int) jitter_rfc/1000, (long unsigned int) jitter_min/1000, (long unsigned int) jitter_avg/1000, (long unsigned int) jitter_max/1000); break; case BAR: print_jitterbar(jitter_rfc/1000, drop_last); break; case NCURSES: // would be nice...? break; default: break; } // Determine whether some packets got lost: // // // // /// PRINT IN FILE_1: statistics only /// if (rtp_log) { ts_hms[0]=0x00; timestamp_hms (ts_hms); fprintf(fp, "%s, %lu, %lu, %lu, %li, %u, %u\n", ts_hms, (long unsigned int) jitter_min/1000, (long unsigned int) jitter_avg/1000, (long unsigned int) jitter_max/1000, (long int) jitter_rfc/1000, drop, dis); fflush(fp); } // Open another file if current file reaches a limit // if ((rtp_log==2) && (gtotal>MAX_DATA_BLOCKS)) { // file big enough, gtotal=0; if (fclose(fp2) == EOF) { perror("fclose"); exit(1); } if (verbose) fprintf(stderr, " mz: %s written.\n",filename); timestamp_human(filename, "rtp_"); // get a new filename strncpy(dummy, path, 128); strncat(dummy, filename, 64); if (verbose) fprintf(stderr, " mz: Will open %s\n", dummy); if ( (fp2 = fopen (dummy, "w+")) == NULL) { if (errno != EAGAIN) { perror("fopen"); exit (-1); } } fprintf(fp2, "# Jitter measurements by Mausezahn " MAUSEZAHN_VERSION_SHORT ".\n"); fprintf(fp2, "# Timestamp (usec) , true jitter (nsec)\n"); } } // statistics end *********************************************************************