/* rtp.cpp
 *
 * Copyright (C) DFS Deutsche Flugsicherung (2004, 2005). 
 * All Rights Reserved.
 * Author: Andre Adrian
 *
 * subset of Real Time Protocol Version 2 (RFC3550)
 * handling of extension and padding is missing
 *
 * Version 0.3
 */

#include <stdio.h>              /* printf() */
#include <stdlib.h>

#include <sys/types.h>          /* u_long */
#include <sys/time.h>           /* gettimeofday() */
#include <unistd.h>             /* get..() */
#include <time.h>               /* clock() */
#include <sys/utsname.h>        /* uname() */
#include <netinet/in.h>
#include <assert.h>

#include "intercomd.h"
#include "rtp.h"

/*
 * Return random unsigned 32-bit quantity.  Use 'type' argument if
 * you need to generate several different values in close succession.
 * (partly from RFC 3550 A.6 Generating a Random 32-bit Identifier)
 */
unsigned long random32(int type)
{
  struct {
    int type;
    struct timeval tv;
    clock_t cpu;
    pid_t pid;
    u_long hid;
    uid_t uid;
    gid_t gid;
    struct utsname name;
  } s;

  gettimeofday(&s.tv, 0);
  uname(&s.name);
  s.type = type;
  s.cpu = clock();
  s.pid = getpid();
  s.hid = gethostid();
  s.uid = getuid();
  s.gid = getgid();
  /* also: system uptime */

  unsigned long *us = (unsigned long *) &s;

  /* use xor to make a (bad) random */
  /* Note: remainder of long div of 32bit prim is better */
  unsigned long random = 0;
  unsigned int i;
  for (i = 0; i < sizeof(s) / sizeof(long); ++i) {
    random ^= *us++;
  }
  return random;
}                               /* random32 */


RTP::RTP()
{
  version = 2;
  padding = 0;
  extension = 0;
  csrc_count = 0;
  marker = 0;
  payload_type = PT_PCMU;
  ssrc = 0;
  sequence = 0;
  timestamp = 0;
}

void RTP::init(int payload_type_, unsigned long ssrc_)
{
  version = 2;
  padding = 0;
  extension = 0;
  csrc_count = 0;
  marker = 0;
  payload_type = payload_type_;
  ssrc = ssrc_;
  /*  My interpretation of RFC3550 A.6: one seed is good enough */
  sequence = random32(payload_type_);
  timestamp = sequence;
}

void RTP::next(int frameduration)
{
  ++sequence;
  timestamp += frameduration;
}

void RTP::reset_csrc()
{
  csrc_count = 0;
}
 
int RTP::add_csrc(unsigned long csrc_)
{
  return_if (csrc_count >= 15, ERROR);
  csrc[csrc_count++] = csrc_;
  
  // printf("add_csrc = %08x\n", csrc_);
  return OKAY;
}

int RTP::find_csrc(unsigned long ssrc_)
{
  unsigned int i;
  
  if (0 == csrc_count) return NO;
  // printf("find_csrc = %08x ", ssrc_);
  for (i = 0; i < csrc_count; ++i) {
    // printf("%08x ", csrc[i]);
    if (csrc[i] == ssrc_) {
      // printf("hit\n");
      return YES;
    }
  }
  return NO;
}

int RTP::check()
{
  // RFC3550 A.1 RTP Data Header Validity Checks

  // RTP version field must equal 2.
  if (version != 2)
    return -1;

  // The payload type must be known
  switch (payload_type) {
    case PT_PCMU:
    case PT_GSM:
    case PT_PCMA:
    case PT_G729:
    case PT_iLBC:
    case PT_EFR: 
    case PT_G726:
    case PT_SPX:
      /* do nothing */
      break;
    default:
      return -1;
  }    

  // The X bit must be zero if the profile does not specify that the
  // header extension mechanism may be used.  Otherwise, the extension
  // length field must be less than the total packet size minus the
  // fixed header length and padding.
  if (extension)
    return -1;                  // hack!

  // The length of the packet must be consistent with CC and payload
  // type (if payloads have a known length).
  // equal to SR or RR.
  // if (csrc_count != 0)
  //  return -1;                  // hack!

  // If the P bit is set, then the last octet of the packet must
  // contain a valid octet count, in particular, less than the total
  // packet length minus the header size.
  if (padding)
    return -1;                  // hack!
  return 0;
}

char *RTP_network_copy(char *to, RTP * from)
{
  unsigned long *ulfrom = (unsigned long *) from;
  unsigned long *ulto = (unsigned long *) to;
  int i;
  for (i = 0; i < 3 + from->get_cc(); ++i) {
    *ulto++ = htonl(*ulfrom++);
  }
  return (char *) ulto;
}

char *RTP_host_copy(RTP * to, char *from)
{
  unsigned long *ulfrom = (unsigned long *) from;
  unsigned long *ulto = (unsigned long *) to;
  int i;
  for (i = 0; i < 3; ++i) {
    *ulto++ = ntohl(*ulfrom++);
  }
  for (i = 0; i < to->get_cc(); ++i) {
    *ulto++ = ntohl(*ulfrom++);
  }
  return (char *) ulfrom;
}
