/* * synchronize is used to adjust a video broadcasts time based * on an audio broadcasts time. * We now start the audio and video just based on the Unix time of the * first packet. Then we use this to adjust when both sides have rtcp * packets. * It will also work if we never get in RTCP - this routine won't be * called - but our sync will be off. */ void CRtpByteStreamBase::synchronize (rtcp_sync_t *sync) { // need to recalculate m_first_pak_ts here uint64_t adjust_first_pak_ts; int64_t adjust_first_pak; int64_t audio_diff, our_diff; if (sync == NULL) { if (!m_have_sync_info) return; sync = &m_sync_info; } else { if (m_rtcp_received == false) m_sync_info = *sync; } m_have_sync_info = true; if (m_psptr != NULL) return; if (m_rtcp_received == false) return; if (m_have_first_pak_ts == false) return; // First calculation - use the first packet's timestamp to calculate // what the timestamp value would be at the RTCP's RTP timestamp value // adjust_first_pak is amount we need to add to the first_packet's timestamp // We do this for the data we got for the audio stream adjust_first_pak = sync->rtcp_rtp_ts; adjust_first_pak -= sync->first_pak_rtp_ts; adjust_first_pak *= 1000; adjust_first_pak /= (int64_t)sync->timescale; adjust_first_pak_ts = sync->first_pak_ts; adjust_first_pak_ts += adjust_first_pak; // Now, we compute the difference between that value and what the RTCP // says the timestamp should be audio_diff = adjust_first_pak_ts; audio_diff -= sync->rtcp_ts; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - audio rtcp rtp ts %u first pak %u", m_name, sync->rtcp_rtp_ts, sync->first_pak_rtp_ts); rtp_message(LOG_DEBUG, "%s - audio rtcp ts "U64" first pak "U64, m_name, sync->rtcp_ts, sync->first_pak_ts); rtp_message(LOG_DEBUG, "%s - adjusted first pak "D64" ts "U64, m_name, adjust_first_pak, sync->timescale); rtp_message(LOG_DEBUG, "%s - diff "D64, m_name, audio_diff); #endif // Now, we do the same calculation for the numbers for our timestamps - // find the timestamp by adjusting the first packet's timestamp to the // timestamp based on the current RTCP RTP timestamp; adjust_first_pak = m_rtcp_rtp_ts; adjust_first_pak -= m_first_pak_rtp_ts; adjust_first_pak *= 1000; adjust_first_pak /= (int64_t)m_timescale; adjust_first_pak_ts = m_first_pak_ts; adjust_first_pak_ts += adjust_first_pak; our_diff = adjust_first_pak_ts; our_diff -= m_rtcp_ts; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - our rtcp rtp ts %u first pak %u", m_name, m_rtcp_rtp_ts, m_first_pak_rtp_ts); rtp_message(LOG_DEBUG, "%s - our rtcp ts "U64" first pak "U64, m_name, m_rtcp_ts, m_first_pak_ts); rtp_message(LOG_DEBUG, "%s - adjusted first pak "D64" ts "U64, m_name, adjust_first_pak, m_timescale); rtp_message(LOG_DEBUG, "%s - diff "D64, m_name, our_diff); rtp_message(LOG_INFO, "%s adjusting first pak ts by "D64, m_name, audio_diff - our_diff); #endif // Now, we very simply add the difference between the 2 to get // what the equivalent start time would be. Note that these values // for the first packet are not fixed - they change over time to avoid // wrap issues. m_first_pak_ts += audio_diff - our_diff; }
/* * add_rtp_packet_to_queue() - adds rtp packet to doubly linked lists - * this is used both by the bytestream, and by the player_media when trying * to determine which rtp payload type is being used. */ int add_rtp_packet_to_queue (rtp_packet *pak, rtp_packet **head, rtp_packet **tail, const char *name) { rtp_packet *q; bool inserted = true; int16_t head_diff = 0, tail_diff = 0; #ifdef DEBUG_RTP_PAKS rtp_message(LOG_DEBUG, "%s - m %u pt %u seq %u ts %x len %d", name, pak->rtp_pak_m, pak->rtp_pak_pt, pak->rtp_pak_seq, pak->rtp_pak_ts, pak->rtp_data_len); #endif if (*head == NULL) { // no packets on queue *head = *tail = pak; pak->rtp_next = pak; pak->rtp_prev = pak; } else { // take the differences between the head and tail sequence numbers tail_diff = pak->rtp_pak_seq - (*tail)->rtp_pak_seq; head_diff = pak->rtp_pak_seq - (*head)->rtp_pak_seq; if (head_diff == 0 || tail_diff == 0) { // duplicate of head - ignore it inserted = false; rtp_message(LOG_ERR, "%s - Duplicate of pak sequence #%u", name, pak->rtp_pak_seq); } else if (head_diff > 0 && tail_diff < 0) { // insert in middle q = (*head)->rtp_next; for (q = (*head)->rtp_next; q->rtp_pak_seq - pak->rtp_pak_seq <= 0; q = q->rtp_next); if (q->rtp_pak_seq == pak->rtp_pak_seq) { // duplicate rtp_message(LOG_ERR, "%s - duplicate pak received %u", name, pak->rtp_pak_seq); inserted = false; } else { rtp_message(LOG_DEBUG, "%s - insert %u before %u", name, pak->rtp_pak_seq, q->rtp_pak_seq); // insert in the middle q->rtp_prev->rtp_next = pak; pak->rtp_prev = q->rtp_prev; q->rtp_prev = pak; pak->rtp_next = q; } } else { // not in the middle. Insert between the tail and head (*head)->rtp_prev = pak; (*tail)->rtp_next = pak; pak->rtp_next = *head; pak->rtp_prev = *tail; if (abs(head_diff) > abs(tail_diff)) { *tail = pak; } else if (head_diff < 0 && head_diff > -10) { // head is closer, so, insert at begin rtp_message(LOG_DEBUG, "%s inserting %u at head %u tail %u", name, pak->rtp_pak_seq, (*head)->rtp_pak_seq, (*tail)->rtp_pak_seq); *head = pak; } else { // insert at tail #if 0 if (head_diff > 1000 || head_diff < -1000 || tail_diff > 1000 || tail_diff < -1000) { rtp_message(LOG_DEBUG, "%s inserting %u at tail - head %u tail %u", name, pak->rtp_pak_seq, (*head)->rtp_pak_seq, (*tail)->rtp_pak_seq); } #endif *tail = pak; } } } if (inserted == false) { rtp_message(LOG_ERR, "%s Couldn't insert pak", name); rtp_message(LOG_DEBUG, "pak seq %u", pak->rtp_pak_seq); if (*head != NULL) { rtp_message(LOG_DEBUG, "head seq %u, tail seq %u", (*head)->rtp_pak_seq, (*tail)->rtp_pak_seq); } xfree(pak); return (0); } q = *head; if (q->rtp_next == *head) return 1; #ifdef DEBUG_SEQUENCE_DROPS bool dump_list = false; int16_t diff; do { diff = q->rtp_next->rtp_pak_seq - q->rtp_pak_seq; if (diff < 0 || diff > 2) { dump_list = true; } else q = q->rtp_next; } while (dump_list == false && q != *tail); if (dump_list) { rtp_message(LOG_DEBUG, "%s possible queue error - inserted %u %d %d", name, pak->rtp_pak_seq, head_diff, tail_diff); rtp_message(LOG_DEBUG, "%s seq %u %u diff %d", name, q->rtp_pak_seq, q->rtp_next->rtp_pak_seq, diff); #if 0 q = *head; do { head_diff = q->rtp_next->rtp_pak_seq - q->rtp_pak_seq; rtp_message(LOG_DEBUG, "%u diff next %d", q->rtp_pak_seq, head_diff); q = q->rtp_next; } while (q != *head); #endif rtp_message(LOG_DEBUG, "%s - head %u tail %u", name, (*head)->rtp_pak_seq, (*tail)->rtp_pak_seq); } #endif return (1); }
/* * recv_callback - callback for when bytestream is active - basically, * put things on the queue */ int CRtpByteStreamBase::recv_callback (struct rtp *session, rtp_event *e) { switch (e->type) { case RX_RTP: rtp_packet *rpak; rpak = (rtp_packet *)e->data; if (rpak->rtp_data_len == 0) { xfree(rpak); } else { // need to add lock/unlock of mutex here if (m_have_recv_last_ts) { int32_t diff = rpak->rtp_pak_ts - m_recv_last_ts; int32_t ts, nts; ts = m_timescale * 2; nts = 0 - ts; if (diff > ts || diff < nts) { rtp_message(LOG_INFO, "%s - rtp timestamp diff %d last %u now %u", m_name, diff, m_recv_last_ts, rpak->rtp_pak_ts); flush_rtp_packets(); reset(); } } m_have_recv_last_ts = true; m_recv_last_ts = rpak->rtp_pak_ts; if (m_buffering == 0) { rpak->pd.rtp_pd_timestamp = get_time_of_day(); rpak->pd.rtp_pd_have_timestamp = 1; } if (SDL_mutexP(m_rtp_packet_mutex) == -1) { rtp_message(LOG_CRIT, "SDL Lock mutex failure in rtp bytestream recv"); break; } add_rtp_packet_to_queue(rpak, &m_head, &m_tail, m_name); if (SDL_mutexV(m_rtp_packet_mutex) == -1) { rtp_message(LOG_CRIT, "SDL Lock mutex failure in rtp bytestream recv"); break; } m_recvd_pak = true; check_buffering(); } break; case RX_SR: rtcp_sr *srpak; srpak = (rtcp_sr *)e->data; if (rtp_my_ssrc(session) != e->ssrc) { //rtp_message(LOG_DEBUG, "%s received rtcp", m_name); calculate_wallclock_offset_from_rtcp(srpak->ntp_frac, srpak->ntp_sec, srpak->rtp_ts); } break; case RX_APP: free(e->data); break; default: #if 0 rtp_message(LOG_DEBUG, "Thread %u - Callback from rtp with %d %p", SDL_ThreadID(),e->type, e->rtp_data); #endif break; } return m_buffering; }
void CRtpByteStreamBase::set_wallclock_offset (uint64_t wclock, uint32_t rtp_ts) { int32_t rtp_ts_diff; int64_t wclock_diff; uint64_t wclock_calc; bool set = true; bool had_recvd_rtcp; if (m_rtcp_received == 1 /*&& m_stream_ondemand == 0*/) { rtp_ts_diff = rtp_ts; rtp_ts_diff -= m_rtcp_rtp_ts; wclock_diff = (int64_t)rtp_ts_diff; wclock_diff *= TO_D64(1000); wclock_diff /= (int64_t)m_timescale; wclock_calc = m_rtcp_ts; wclock_calc += wclock_diff; set = false; if (wclock_calc != wclock) { #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - set wallclock - wclock should be "U64" is "U64, m_name, wclock_calc, wclock); #endif // don't change wclock offset if it's > 100 msec - otherwise, // it's annoying noise int64_t diff = wclock_calc - wclock; if (abs(diff) > 2 && abs(diff) < 100) { set = false; // rtp_message(LOG_DEBUG, "not changing"); // we'll allow a msec drift here or there to allow for rounding - // we want this to change every so often } } } had_recvd_rtcp = m_rtcp_received; m_rtcp_received = true; SDL_LockMutex(m_rtp_packet_mutex); if (set) { m_rtcp_ts = wclock; m_rtcp_rtp_ts = rtp_ts; } if (m_have_first_pak_ts) { // we only want positives here int32_t diff; diff = rtp_ts - m_first_pak_rtp_ts; int32_t compare = 3600 * m_timescale; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "%s - 1st rtp ts %u rtp %u %u", m_name, m_first_pak_rtp_ts, rtp_ts, diff); rtp_message(LOG_DEBUG, "%s - 1st ts "U64, m_name, m_first_pak_ts); #endif if (diff > compare) { // adjust once an hour, to keep errors low // we'll adjust the timestamp and rtp timestamp int64_t ts_diff; ts_diff = (int64_t)diff; ts_diff *= TO_U64(1000); ts_diff /= (int64_t)m_timescale; m_first_pak_ts += ts_diff; m_first_pak_rtp_ts += diff; #ifdef DEBUG_RTP_WCLOCK rtp_message(LOG_DEBUG, "CHANGE %s - first pak ts is now "U64" rtp %u", m_name, m_first_pak_ts, m_first_pak_rtp_ts); #endif } // We've received an RTCP - see if we need to syncronize // the video streams. if (m_psptr != NULL) { rtcp_sync_t sync; sync.first_pak_ts = m_first_pak_ts; sync.first_pak_rtp_ts = m_first_pak_rtp_ts; sync.rtcp_ts = m_rtcp_ts; sync.rtcp_rtp_ts = m_rtcp_rtp_ts; sync.timescale = m_timescale; m_psptr->synchronize_rtp_bytestreams(&sync); } else { // if this is our first rtcp, try to synchronize if (!had_recvd_rtcp) synchronize(NULL); } } SDL_UnlockMutex(m_rtp_packet_mutex); }
void CAudioRtpByteStream::reset (void) { rtp_message(LOG_DEBUG, "in audiortpreset"); CRtpByteStream::reset(); }
static socket_udp *udp_init4(const char *addr, const char *iface, uint16_t rx_port, uint16_t tx_port, int ttl) { int reuse = 1; struct sockaddr_in s_in; int recv_buf_size; int test_buffer; int test_buffer_size=sizeof(test_buffer); socket_udp *s = (socket_udp *)malloc(sizeof(socket_udp)); s->mode = IPv4; s->addr = NULL; s->rx_port = rx_port; s->tx_port = tx_port; s->ttl = ttl; if (inet_pton(AF_INET, addr, &s->addr4) != 1) { struct hostent *h = gethostbyname(addr); if (h == NULL) { socket_error("Can't resolve IP address for %s", addr); free(s); return NULL; } memcpy(&(s->addr4), h->h_addr_list[0], sizeof(s->addr4)); } if (iface != NULL) { if (inet_pton(AF_INET, iface, &s->iface4_addr) != 1) { rtp_message(LOG_ERR, "Illegal interface specification"); free(s); return NULL; } } else { s->iface4_addr.s_addr = 0; } s->fd = socket(AF_INET, SOCK_DGRAM, 0); if (s->fd < 0) { socket_error("socket"); free(s); return NULL; } if (have_recv_buf_size != 0) { recv_buf_size = recv_buf_size_value; if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(int)) != 0) { socket_error("setsockopt SO_RCVBUF"); close(s->fd); free(s); return NULL; } //Since setsockopt would not return the error if /proc/sys/net/core/rmem_max is smaller //then the value you are trying to set. use sysctl -w net.core.rmem_max=new_val //to set the value higher than what is desired in RCVBUF if( getsockopt( s->fd, SOL_SOCKET, SO_RCVBUF, (void*)&test_buffer, &test_buffer_size ) == -1 ) { socket_error("getsockopt SO_RCVBUF"); } else { //See if we could set the desired value if(test_buffer < recv_buf_size) { rtp_message(LOG_WARNING, "Failed to set the RCVBUF to %d, only could set %d\n. Check the Max kernel receive buffer size using \"sysctl net.core.rmem_max\"\n", recv_buf_size, test_buffer); } } } if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) != 0) { socket_error("setsockopt SO_REUSEADDR"); close(s->fd); free(s); return NULL; } #ifdef SO_REUSEPORT if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_REUSEPORT, (char *) &reuse, sizeof(reuse)) != 0) { close(s->fd); free(s); socket_error("setsockopt SO_REUSEPORT"); return NULL; } #endif s_in.sin_family = AF_INET; s_in.sin_addr.s_addr = INADDR_ANY; s_in.sin_port = htons(rx_port); if (bind(s->fd, (struct sockaddr *) &s_in, sizeof(s_in)) != 0) { socket_error("bind: port %d", rx_port); close(s->fd); free(s); return NULL; } if (IN_MULTICAST(ntohl(s->addr4.s_addr))) { char loop = 1; #ifndef HAVE_IGMP_V3 struct ip_mreq imr; imr.imr_multiaddr.s_addr = s->addr4.s_addr; imr.imr_interface.s_addr = s->iface4_addr.s_addr; if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)) != 0) { socket_error("setsockopt IP_ADD_MEMBERSHIP"); close(s->fd); free(s); return NULL; } #else rtp_message(LOG_DEBUG,"IGMPV3 src:%s\n", G_Multicast_Src); /* Join Multicast group with source filter */ if (G_IGMP_V3 != 0 && strcmp(G_Multicast_Src, "0.0.0.0") != 0) { struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = s->addr4.s_addr; imr.imr_interface.s_addr = s->iface4_addr.s_addr; if (inet_aton(G_Multicast_Src, &imr.imr_sourceaddr) == 0) { rtp_message(LOG_ERR, "inet_aton failed for %s\n", G_Multicast_Src); return NULL; } if( setsockopt( s->fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)&imr, sizeof(struct ip_mreq_source) ) == -1 ) { socket_error("setsockopt IP_ADD_SOURCE_MEMBERSHIP"); close(s->fd); free(s); return NULL; } } #endif #ifndef WIN32 if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) != 0) { socket_error("setsockopt IP_MULTICAST_LOOP"); close(s->fd); free(s); return NULL; } #endif if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &s->ttl, sizeof(s->ttl)) != 0) { socket_error("setsockopt IP_MULTICAST_TTL"); close(s->fd); free(s); return NULL; } if (s->iface4_addr.s_addr != 0) { if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &s->iface4_addr, sizeof(s->iface4_addr)) != 0) { close(s->fd); free(s); socket_error("setsockopt IP_MULTICAST_IF"); return NULL; } } } s->addr = strdup(addr); return s; }