/* The library calls this function when it needs to schedule an event. * * Note that if we were running more than one stream at once we'd need * some way of corrolating the event to run with the context ID this * function gets passed. */ void RTPSchedule(context cid, rtp_opaque_t opaque, struct timeval *tp) { struct evt_queue_elt *elt; elt = (struct evt_queue_elt *) malloc(sizeof(struct evt_queue_elt)); if (elt == NULL) return; elt->cid = cid; elt->event_opaque = opaque; elt->event_time = tv2dbl(*tp); insert_in_evt_queue(elt); return; }
void RtpSession::send_rtcp() { RtcpPacket *p; struct timeval tv; gettimeofday(&tv, NULL); ntp_t ntp = timeval_to_ntp( tv ); uint8_t type = m_event_queue->type; switch ( m_event_queue->type ) { case RTCP_SR: /* We want to send an RTCP Sender Report. */ p = new RtcpPacket( RTCP_SR ); p->set_count( 0 ); p->set_sr_ssrc( m_ssrc ); p->set_sr_ntp_sec( ntp.secs ); p->set_sr_ntp_frac( ntp.frac ); p->set_sr_rtp_ts( m_last_timestamp ); p->set_sr_psent( m_psent ); p->set_sr_osent( m_osent ); break; case RTCP_RR: p = new RtcpPacket( RTCP_RR ); p->set_count( 1 ); default: p = NULL; } if ( ! p ) return; send_rtcp_pkt( p ); /* Advance the queue */ event_t *next = (event_t *)m_event_queue->next; free( m_event_queue ); m_event_queue = next; /* Schedule a new RTCP packet */ event_t *e = (event_t*)malloc( sizeof( event_t ) ); e->type = type; e->time = tv2dbl( tv ) + compute_rtcp_interval(); e->next = NULL; insert_event( e ); }
uint16_t RtpSession::receive_rtp_packet(void *buf ) { struct timeval start_time_tv; if ( ! m_preceived ) { /* This is the 1th packet that we receive- * Here we should send an RTCP SDES packet with all the * infos... */ gettimeofday(&start_time_tv, NULL); m_start_time = tv2dbl(start_time_tv); #if 0 /*! Build the SDES CNAME packet */ uint16_t s = 10 + strlen( m_cname ); /* Align to 32 bit boundary .. */ s = (uint16_t)ceil( (double)s /4 ) * 4; m_sdes_pkt = (rtcp_t *)calloc( s , 1 ); RtcpPacket *p = new RtcpPacket( m_sdes_pkt, s ); p->set_version( 2 ); p->set_padding( 0 ); p->set_count( 1 ); p->set_type( RTCP_SDES ); p->set_length( (uint16_t)s/4 - 1 ); m_sdes_pkt->r.sdes.src = htonl( m_ssrc ); m_sdes_pkt->r.sdes.item->type = RTCP_SDES_CNAME; m_sdes_pkt->r.sdes.item->length = strlen( m_cname ); memcpy( m_sdes_pkt->r.sdes.item[0].data, m_cname, strlen(m_cname) ); m_sdes_pkt_size = s; /*! Schedule an RTCP RR.. */ event_t *e = (event_t*)malloc( sizeof( event_t ) ); e->type = RTCP_RR; e->time = m_start_time + 5.0; e->next = NULL; insert_event( e ); send_rtcp(); #endif } int event = 0; int retval; double timeout, now; struct timeval timeout_tv, now_tv; fd_set sockets; /*! Here we check for events or things to do... */ while ( 1 ) { gettimeofday(&now_tv, NULL); now = tv2dbl(now_tv); if ( m_event_queue != NULL ) { event = 1; timeout = m_event_queue->time - now; } else { event = 0; timeout = 0.5; /* Arbitrary value.. to be trimmed.. */ } if ( timeout < 0 ) timeout = 0; timeout_tv = dbl2tv( timeout ); FD_ZERO( &sockets ); FD_SET( m_rtp_sock, &sockets ); FD_SET( m_rtcp_sock, &sockets ); int max_fd = ((m_rtp_sock > m_rtcp_sock) ? m_rtp_sock : m_rtcp_sock ) + 1; retval = select(max_fd, &sockets, NULL, NULL, &timeout_tv); if ( retval < 0 ) { perror("select"); return false; } else if ( retval > 0 ) { // There are some events... if (FD_ISSET(m_rtp_sock, &sockets)) { /* There's an RTP packet to be read... */ return receive_rtp( buf ); } if (FD_ISSET(m_rtcp_sock, &sockets)) { receive_rtcp(); } } else { /* retval == 0, select timed out */ if (event) { gettimeofday( &now_tv, NULL ); now = tv2dbl( now_tv ); while ( m_event_queue != NULL && m_event_queue->time <= now ) { /* There is a pending RTCP packet to send) */ send_rtcp(); } } } } }
bool RtpSession::send_rtp_packet(void *buf, uint16_t size, uint32_t ts_inc ) { uint32_t timestamp; struct timeval start_time_tv, now_tv; double play_time, now; timestamp = m_prev_timestamp + ts_inc; m_prev_timestamp = timestamp; if ( !buf || !size ) { printf("Cannot send packet.\n"); return false; } if ( ! m_psent ) { /* This is the 1th packet that we send. * Here we should send an RTCP SDES packet with all the * infos... */ gettimeofday(&start_time_tv, NULL); m_start_time = tv2dbl(start_time_tv); /*! Build the SDES CNAME packet */ uint16_t s = 10 + strlen( m_cname ); /* Align to 32 bit boundary .. */ s = (uint16_t)ceil( (double)s /4 ) * 4; m_sdes_pkt = (rtcp_t *)calloc( s , 1 ); RtcpPacket *p = new RtcpPacket( m_sdes_pkt, s ); p->set_version( 2 ); p->set_padding( 0 ); p->set_count( 1 ); p->set_type( RTCP_SDES ); p->set_length( (uint16_t)s/4 - 1 ); m_sdes_pkt->r.sdes.src = htonl( m_ssrc ); m_sdes_pkt->r.sdes.item->type = RTCP_SDES_CNAME; m_sdes_pkt->r.sdes.item->length = strlen( m_cname ); memcpy( m_sdes_pkt->r.sdes.item[0].data, m_cname, strlen(m_cname) ); m_sdes_pkt_size = s; /*! Schedule an RTCP for now.. */ event_t *e = (event_t*)malloc( sizeof( event_t ) ); e->type = RTCP_SR; e->time = 0.0; e->next = NULL; insert_event( e ); send_rtcp(); } /** OK, we build the real RTP packet... */ RtpPacket *pkt = new RtpPacket( (uint8_t*)buf, size ); pkt->set_ssrc( m_ssrc ); pkt->set_payload_type( m_payload_type ); pkt->set_sequence( m_seq++ ); pkt->set_timestamp( timestamp ); delete pkt; if ( ! send_pkt( buf, size ) ) { /* Problems sending packet.. probably the client closed * the connection.. */ return false; } /* *************************************************** */ /*! Here we check for events or things to do... */ /* Schedule the times to play packets as an absolute offset from * our start time, rather than a relative offset from the initial * packet. (We're less vulnerable to drifting clocks that way). * Alternative version based on timestamps and RTP clock.. */ play_time = m_start_time + ((double)(timestamp - m_base_timestamp)/ m_rtp_clock); // printf("play_time: %f - difference: %f sec.\n", play_time, // ((double)(timestamp - m_base_timestamp)/ m_rtp_clock)); while (gettimeofday(&now_tv, NULL), (now = tv2dbl(now_tv)) < play_time) { int event = 0; int retval; double timeout; struct timeval timeout_tv; fd_set sockets; if ( m_event_queue != NULL && m_event_queue->time < play_time ) { event = 1; timeout = m_event_queue->time - now; } else { event = 0; timeout = play_time - now; } if ( timeout < 0 ) timeout = 0; timeout_tv = dbl2tv( timeout ); FD_ZERO( &sockets ); FD_SET( m_rtp_sock, &sockets ); FD_SET( m_rtcp_sock, &sockets ); int max_fd = ((m_rtp_sock > m_rtcp_sock) ? m_rtp_sock : m_rtcp_sock ) + 1; retval = select(max_fd, &sockets, NULL, NULL, &timeout_tv); if ( retval < 0 ) { perror("select"); exit(1); } else if ( retval > 0 ) { // There are some events... if (FD_ISSET(m_rtp_sock, &sockets)) { /* There's an RTP packet to be read... * We should receive, validate and .. trash it.. */ receive_rtp( (void*)m_buf ); } if (FD_ISSET(m_rtcp_sock, &sockets)) { receive_rtcp(); } } else { /* retval == 0, select timed out */ if (event) { gettimeofday( &now_tv, NULL ); now = tv2dbl( now_tv ); while ( m_event_queue != NULL && m_event_queue->time <= now ) { /* There is a pending RTCP packet to send) */ send_rtcp(); } } else break; /* Time for the next packet */ } } /* **************************************************** */ return true; }
int main(int argc, char *argv[]) { /* The context id the RTP library gives us to keep track of our RTP stream. */ context cid; /* The two socket fds (RTP and RTCP) we need to select on. */ socktype sock[2]; /* The highest file descriptor we've seen, of those we need to select on. */ int nfds; /* The audio file we're playing. */ FILE *input; /* Play 20 ms packets outgoing. */ #define BUFFER_SIZE 160 /* The buffer for our outgoing data. */ char buffer[BUFFER_SIZE]; /* Maximum size of a UDP packet. */ #define RECEIVE_BUFFER_SIZE 65536 /* The buffer incoming data accumulates in. We don't actually care * about the contents of this data, but we must call RTPReceive() so our * sender reports and receiver reports are correct. */ char receive_buffer[RECEIVE_BUFFER_SIZE]; /* Some values for keeping track of amount of data read from the file */ int bytes_read, total_read, last_read; /* Some values for keeping track of when to send packets. */ struct timeval start_time_tv, now_tv; double start_time, play_time, now; /* Information about the media stream we're using. */ double sample_rate, byte_rate; /* The amount to increment the RTP timestamp for a given packet */ int tsinc; /* The RTP marker bit; initially, it's set. */ char marker = 1; /* Error values that the RTP library returns. */ rtperror err; /* 8khz 8-bit mulaw */ sample_rate = byte_rate = 1/8000.0; if (argc < 3) { fprintf(stderr, "usage: %s file port\n", argv[0]); exit(2); } input = fopen(argv[1], "r"); if (input == NULL) { perror(argv[1]); exit(1); } if ((nfds = setup_connection(argv[2], &cid, sock)) < 0) { exit(1); } total_read = 0; last_read = BUFFER_SIZE; /* Arbitrary initial setting. */ gettimeofday(&start_time_tv, NULL); start_time = tv2dbl(start_time_tv); while((bytes_read = fread(buffer, 1, BUFFER_SIZE, input)) > 0) { /* The tsinc (time-stamp increment) is equal to the number of samples * played in the *previous* packet -- it's the amount to increment the * RTP timestamp from the previous packet to this one. */ tsinc = last_read * (sample_rate/byte_rate); /* Send the data we just read to the network. * We're assuming here that the payload is always Mulaw-8. * See comments above for explanations of the other arguments. */ err = RTPSend(cid, tsinc, marker, PAYLOAD_TYPE_MULAW_8, buffer, bytes_read); if (err != RTP_OK) { fprintf(stderr, "%s\n", RTPStrError(err)); exit(1); } /* The marker bit doesn't get set for any packet but the first. */ marker = 0; total_read += bytes_read; last_read = bytes_read; /* Schedule the times to play packets as an absolute offset from * our start time, rather than a relative offset from the initial * packet. (We're less vulnerable to drifting clocks that way). */ play_time = start_time + (total_read * byte_rate); /* Handle RTP events and received RTP and RTCP packets until the next * play time. */ while (gettimeofday(&now_tv, NULL), (now = tv2dbl(now_tv)) < play_time) { int event; int retval, i; double timeout; struct timeval timeout_tv; fd_set sockets; /* If we have pending events which are coming before the next packet, * timeout for them rather than for the next packet to play. */ if (evt_queue != NULL && evt_queue->event_time < play_time) { event = 1; timeout = evt_queue->event_time - now; } else { event = 0; timeout = play_time - now; } if (timeout < 0) timeout = 0; timeout_tv = dbl2tv(timeout); FD_ZERO(&sockets); FD_SET(sock[0], &sockets); FD_SET(sock[1], &sockets); retval = select(nfds + 1, &sockets, NULL, NULL, &timeout_tv); if (retval < 0) { /* select returned an error */ perror("select"); exit(1); } else if (retval > 0) { /* select reported readable fd's. */ for (i = 0; i < 2; i++) { if (FD_ISSET(sock[i], &sockets)) { int recbuflen = RECEIVE_BUFFER_SIZE; /* We don't care about the contents of the received packet; * we're only a sender. However, we still have to call * RTPReceive to ensure that our sender reports and * receiver reports are correct. */ err = RTPReceive(cid, sock[i], receive_buffer, &recbuflen); if (err != RTP_OK && err != RTP_PACKET_LOOPBACK) { fprintf(stderr, "RTPReceive %s (%d): %s\n", i ? "RTCP" : "RTP", sock[i], RTPStrError(err)); } } } } else { /* retval == 0, select timed out */ if (event) { struct evt_queue_elt *next; gettimeofday(&now_tv, NULL); now = tv2dbl(now_tv); while (evt_queue != NULL && evt_queue->event_time <= now) { /* There is a pending RTP event (currently this means there's * an RTCP packet to send), so run it. */ RTPExecute(evt_queue->cid, evt_queue->event_opaque); /* Advance the queue */ next = evt_queue->next; free(evt_queue); evt_queue = next; } } else break; /* Time for the next audio packet */ } } } /* We're done sending now, so close the connection. */ /* This sends the RTCP BYE packet and closes our sockets. */ if ((err = RTPCloseConnection(cid, "Goodbye!")) != RTP_OK) { fprintf(stderr, "%s\n", RTPStrError(err)); } /* De-allocate all the data structures reserved for the connection. * (This would also close the connection, as well, if we hadn't just done * it above.) */ if ((err = RTPDestroy(cid)) != RTP_OK) { fprintf(stderr, "%s\n", RTPStrError(err)); } return 0; }
int main(int argc, char *argv[]) { /* The context id the RTP library gives us to keep track of our RTP stream. */ context cid; /* The two socket fds (RTP and RTCP) we need to select on. */ socktype sock[2]; /* The highest file descriptor we've seen, of those we need to select on. */ int nfds; /* Maximum size of a UDP packet. */ #define RECEIVE_BUFFER_SIZE 65536 /* The buffer incoming data accumulates in. We don't actually care * about the contents of this data, but we must call RTPReceive() so our * sender reports and receiver reports are correct. */ char receive_buffer[RECEIVE_BUFFER_SIZE]; /* Some values for keeping track of when to send packets. */ struct timeval now_tv; double now; /* Error values that the RTP library returns. */ rtperror err = RTP_OK; #ifdef WIN32 WORD wVersionRequested; WSADATA wsaData; int err2; wVersionRequested = MAKEWORD( 2, 0 ); err2 = WSAStartup( wVersionRequested, &wsaData ); if ( err2 != 0 ) { /* Tell the user that we couldn't find a usable */ /* WinSock DLL. */ return(-1); } #endif if (argc < 2) { fprintf(stderr, "usage: %s address/port/ttl\n", argv[0]); exit(2); } if ((nfds = setup_connection(argv[1], &cid, sock)) < 0) { exit(1); } /* Set up our signal handler. */ signal(SIGINT, done); signal(SIGTERM, done); #ifdef __unix signal(SIGHUP, done); #endif while (!finished) { int event; int retval, i; double timeout; struct timeval timeout_tv, *timeout_tvp; fd_set sockets; gettimeofday(&now_tv, NULL); now = tv2dbl(now_tv); /* If we have pending events which are coming before the next packet, * timeout for them rather than for the next packet to play. */ if (evt_queue != NULL) { event = 1; timeout = evt_queue->event_time - now; if (timeout < 0) timeout = 0; timeout_tv = dbl2tv(timeout); timeout_tvp = &timeout_tv; } else { event = 0; timeout_tvp = NULL; } FD_ZERO(&sockets); FD_SET(sock[0], &sockets); FD_SET(sock[1], &sockets); retval = select(nfds + 1, &sockets, NULL, NULL, timeout_tvp); if (retval < 0) { /* select returned an error */ #ifdef __unix if (errno == EINTR) { /* We probably got a signal */ continue; } #endif /* Otherwise something's wrong */ perror("select"); exit(1); } else if (retval > 0) { /* select reported readable fd's. */ for (i = 0; i < 2; i++) { if (FD_ISSET(sock[i], &sockets)) { int recbuflen = RECEIVE_BUFFER_SIZE; /* We don't care about the contents of the received packet per se. * However, all of our callbacks, reporting information about other * members, are called by code that occurs underneath RTPReceive. * Plus this makes sure all our reciver reports are correct. */ err = RTPReceive(cid, sock[i], receive_buffer, &recbuflen); /* Packet loopback is normal with multicast */ if (err != RTP_OK && err != RTP_PACKET_LOOPBACK) { fprintf(stderr, "RTPReceive %s (%d): %s\n", i ? "RTCP" : "RTP", sock[i], RTPStrError(err)); } if(i == 1) PrintReporting(cid); } } } else { /* retval == 0, select timed out */ if (event) { struct evt_queue_elt *next; gettimeofday(&now_tv, NULL); now = tv2dbl(now_tv); while (evt_queue != NULL && evt_queue->event_time <= now) { /* There is a pending RTP event (currently this means there's * an RTCP packet to send), so run it. */ RTPExecute(evt_queue->cid, evt_queue->event_opaque); /* Advance the queue */ next = evt_queue->next; free(evt_queue); evt_queue = next; } } else continue; /* This shouldn't happen, so just loop. */ } } /* We're done sending now, so close the connection. */ /* This sends the RTCP BYE packet and closes our sockets. */ if ((err = RTPCloseConnection(cid,"Goodbye!")) != RTP_OK) { fprintf(stderr, "%s\n", RTPStrError(err)); } /* De-allocate all the data structures reserved for the connection. * (This would also close the connection, as well, if we hadn't just done * it above.) */ if ((err = RTPDestroy(cid)) != RTP_OK) { fprintf(stderr, "%s\n", RTPStrError(err)); } return 0; }