/* add silence to RTP stream from last packet time to current time which is in header->ts */ void RTP::jt_tail(struct pcap_pkthdr *header) { if(!ast_jb_test(channel_record)) { // there is no ongoing recording, return return; } /* protect for endless loops (it cannot happen in theory but to be sure */ if(packetization <= 0) { Call *owner = (Call*)call_owner; if(owner) { syslog(LOG_ERR, "call-id[%s]: packetization is 0 in jitterbuffer function.", owner->get_fbasename_safe()); } else { syslog(LOG_ERR, "call-id[N/A]: packetization is 0 in jitterbuffer function."); } return; } /* calculate time difference between last packet and current packet + packetization time*/ if(channel_record->last_ts.tv_sec == 0) { // previouuse tv_sec is not set, set it memcpy(&channel_record->last_ts, &header->ts, sizeof(timeval)); return; } int msdiff = ast_tvdiff_ms(header->ts, channel_record->last_ts); msdiff -= packetization; while( msdiff >= packetization ) { ast_jb_get_and_deliver(channel_record, &channel_record->last_ts); /* adding packetization time to last_ts time */ struct timeval tmp = ast_tvadd(channel_record->last_ts, ast_samp2tv(packetization, 1000)); memcpy(&channel_record->last_ts, &tmp, sizeof(struct timeval)); msdiff -= packetization; } }
/* simulate jitterbuffer */ void RTP::jitterbuffer(struct ast_channel *channel, int savePayload) { if(codec == PAYLOAD_TELEVENT) return; Call *owner = (Call*)call_owner; if(owner and savePayload and owner->silencerecording) { // skip recording frame->skip = 1; } else { frame->skip = 0; } struct timeval tsdiff; switch(codec) { case PAYLOAD_OPUS12: frame->ts = getTimestamp() / 12; frame->len = packetization * 2 / 3; break; case PAYLOAD_ISAC16: case PAYLOAD_SILK16: case PAYLOAD_OPUS16: frame->ts = getTimestamp() / 16; frame->len = packetization / 2; break; case PAYLOAD_SILK24: case PAYLOAD_OPUS24: frame->ts = getTimestamp() / 24; frame->len = packetization / 3; break; case PAYLOAD_OPUS48: frame->ts = getTimestamp() / 48; frame->len = packetization / 6; break; default: frame->ts = getTimestamp() / 8; frame->len = packetization; } frame->marker = getMarker(); frame->seqno = getSeqNum(); channel->codec = codec; memcpy(&frame->delivery, &header->ts, sizeof(struct timeval)); /* protect for endless loops (it cannot happen in theory but to be sure */ if(packetization <= 0) { if(pinformed == 0) { if(owner) { syslog(LOG_ERR, "call-id[%s] ssrc[%x]: packetization is 0 in jitterbuffer function.", owner->get_fbasename_safe(), getSSRC()); } else { syslog(LOG_ERR, "call-id[N/A] ssrc[%x]: packetization is 0 in jitterbuffer function.", getSSRC()); } } pinformed = 1; return; } else { pinformed = 0; } struct iphdr2 *header_ip = (struct iphdr2 *)(data - sizeof(struct iphdr2) - sizeof(udphdr2)); int mylen = MIN(len, ntohs(header_ip->tot_len) - header_ip->ihl * 4 - sizeof(udphdr2)); if(savePayload or (codec == PAYLOAD_G729 or codec == PAYLOAD_G723)) { /* get RTP payload header and datalen */ payload_data = data + sizeof(RTPFixedHeader); payload_len = mylen - sizeof(RTPFixedHeader); if(getPadding()) { /* * If set, this packet contains one or more additional padding * bytes at the end which are not part of the payload. The last * byte of the padding contains a count of how many padding bytes * should be ignored. Padding may be needed by some encryption * algorithms with fixed block sizes or for carrying several RTP * packets in a lower-layer protocol data unit. */ payload_len -= ((u_int8_t *)data)[payload_len - 1]; } if(getCC() > 0) { /* * The number of CSRC identifiers that follow the fixed header. */ payload_data += 4 * getCC(); payload_len -= 4 * getCC(); } if(getExtension()) { /* * If set, the fixed header is followed by exactly one header extension. */ extension_hdr_t *rtpext; if (payload_len < 4) payload_len = 0; // the extension, if present, is after the CSRC list. rtpext = (extension_hdr_t *)((u_int8_t *)payload_data); payload_data += sizeof(extension_hdr_t) + rtpext->length; payload_len -= sizeof(extension_hdr_t) + rtpext->length; } frame->data = payload_data; frame->datalen = frame->datalen2 = payload_len > 0 ? payload_len : 0; /* ensure that datalen is never negative */ if(codec == PAYLOAD_G723) { // voipmonitor does not handle SID packets well (silence packets) it causes out of sync if((unsigned char)payload_data[0] & 2) { /* check if jitterbuffer is already created. If not we have to create it because if call starts with SID packets first it will than cause out of sync calls */ if(ast_test_flag(&channel->jb, (1 << 2))) { // jitterbuffer is created so we can skip SID packets now return; } } } if(codec == PAYLOAD_G729 and (payload_len <= 12)) { frame->frametype = AST_FRAME_DTMF; } } else { frame->datalen2 = 0; } if(savePayload) { channel->rawstream = gfileRAW; Call *owner = (Call*)call_owner; if(iscaller) { owner->codec_caller = codec; if(owner->audiobuffer1) { channel->audiobuf = owner->audiobuffer1; } } else { owner->codec_called = codec; if(owner->audiobuffer2) { channel->audiobuf = owner->audiobuffer2; } } if(payload_len > 0) { channel->last_datalen = frame->datalen; } } else { frame->datalen = 0; frame->data = NULL; channel->rawstream = NULL; } // create jitter buffer structures ast_jb_do_usecheck(channel, &header->ts); if(!channel->jb_reseted) { // initializing jitterbuffer if(savePayload) { channel_record->jitter_max = frame->len * 3; } ast_jb_empty_and_reset(channel); channel->jb_reseted = 1; memcpy(&channel->last_ts, &header->ts, sizeof(struct timeval)); ast_jb_put(channel, frame, &header->ts); return; } /* calculate time difference between last packet and current packet + packetization time*/ int msdiff = ast_tvdiff_ms( header->ts, ast_tvadd(channel->last_ts, ast_samp2tv(packetization, 1000)) ); //printf("ms:%d\n", msdiff); if(msdiff > packetization * 1000) { // difference is too big, reseting last_ts to current packet. If we dont check this it could happen to run while cycle endlessly memcpy(&channel->last_ts, &header->ts, sizeof(struct timeval)); ast_jb_put(channel, frame, &header->ts); if(verbosity > 4) syslog(LOG_ERR, "big timestamp jump (msdiff:%d packetization: %d) in this file: %s\n", msdiff, packetization, gfilename); return; } /* between last packet and current packet is big timestamp difference and it could count * interpolated framed although it was silence so calculate real number of packets based * on timestamps in packet header, timestamps in rtp header and sequence numbers between * last packet and current packet */ // relative time difference calculated from packet sequence u_int32_t sequencems = (frame->seqno - last_seq) * packetization; /* difference (in ms) between timestamps in packet header and rtp timestamps. this should * be ideally equel to zero. Negative values mean that packet arrives earlier and positive * values indicates that packet was late */ long double transit = (timeval_subtract(&tsdiff, header->ts, s->lastTimeRecJ) ? -timeval2micro(tsdiff)/1000.0 : timeval2micro(tsdiff)/1000.0) - (double)(getTimestamp() - s->lastTimeStampJ)/(double)samplerate/1000; /* and now if there is bigger (lets say one second) timestamp difference (calculated from packet headers) * between two last packets and transit time is equel or smaller than sequencems (with 200ms toleration), * it was silence and manually mark the frame which indicates to not count interpolated frame and resynchronize jitterbuffer */ if( msdiff > 1000 and (transit <= (sequencems + 200)) ) { if(verbosity > 4) printf("jitterbuffer: manually marking packet, msdiff(%d) > 1000 and transit (%Lf) <= ((sequencems(%u) + 200)\n", msdiff, transit, sequencems); frame->marker = 1; } // fetch packet from jitterbuffer every 20 ms regardless on packet loss or delay while( msdiff >= packetization ) { if(frame->marker or lastframetype == AST_FRAME_DTMF) { /* if last frame was marked or DTMF, ignore interpolated frames */ channel->last_loss_burst = 0; } ast_jb_get_and_deliver(channel, &channel->last_ts); /* adding packetization time to last_ts time */ struct timeval tmp = ast_tvadd(channel->last_ts, ast_samp2tv(frame->len, 1000)); memcpy(&channel->last_ts, &tmp, sizeof(struct timeval)); msdiff -= packetization; } //printf("s[%u] codec[%d]\n",getSeqNum(), codec); ast_jb_put(channel, frame, &header->ts); }