Example #1
0
/*
 * 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;
}
Example #2
0
/*
 * 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);
}
Example #3
0
/*
 * 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;
}
Example #4
0
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);
}
Example #5
0
void CAudioRtpByteStream::reset (void)
{
  rtp_message(LOG_DEBUG, "in audiortpreset");
  CRtpByteStream::reset();
}
Example #6
0
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;
}