class SippyCup::Media

Constants

MSEC
USEC
VALID_STEPS

Attributes

sequence[RW]

Public Class Methods

new(from_addr, from_port, to_addr, to_port, generator = PCMUPayload) click to toggle source
# File lib/sippy_cup/media.rb, line 13
def initialize(from_addr, from_port, to_addr, to_port, generator = PCMUPayload)
  @from_addr, @to_addr = IPAddr.new(from_addr), IPAddr.new(to_addr)
  @from_port, @to_port, @generator = from_port, to_port, generator
  reset!
end

Public Instance Methods

<<(input) click to toggle source
# File lib/sippy_cup/media.rb, line 23
def <<(input)
  get_step input # validation
  @sequence << input
end
compile!() click to toggle source
# File lib/sippy_cup/media.rb, line 32
def compile!
  sequence_number = 0
  start_time = Time.now
  @pcap_file = PacketFu::PcapFile.new
  timestamp = 0
  elapsed = 0
  ssrc_id = rand 2147483648
  first_audio = true

  @sequence.each do |input|
    action, value = get_step input

    case action
    when 'silence'
      # value is the duration in milliseconds
      # append that many milliseconds of silent RTP audio
      (value.to_i / @generator::PTIME).times do
        packet = new_packet
        rtp_frame = @generator.new

        # The first RTP audio packet should have the marker bit set
        if first_audio
          rtp_frame.rtp_marker = 1
          first_audio = false
        end

        rtp_frame.rtp_timestamp = timestamp += rtp_frame.timestamp_interval
        elapsed += rtp_frame.ptime
        rtp_frame.rtp_sequence_num = sequence_number += 1
        rtp_frame.rtp_ssrc_id = ssrc_id
        packet.headers.last.body = rtp_frame.to_bytes
        packet.recalc
        @pcap_file.body << get_pcap_packet(packet, next_ts(start_time, elapsed))
      end
    when 'dtmf'
      # value is the DTMF digit to send
      # append that RFC2833 digit
      # Assume 0.25 second duration for now
      count = 250 / DTMFPayload::PTIME
      count.times do |i|
        packet = new_packet
        dtmf_frame = DTMFPayload.new value
        dtmf_frame.rtp_marker = 1 if i == 0
        dtmf_frame.rtp_timestamp = timestamp # Is this correct? This is what Blink does...
        #dtmf_frame.rtp_timestamp = timestamp += dtmf_frame.timestamp_interval
        dtmf_frame.rtp_sequence_num = sequence_number += 1
        dtmf_frame.rtp_ssrc_id = ssrc_id
        dtmf_frame.end_of_event = (count == i) # Last packet?
        packet.headers.last.body = dtmf_frame.to_bytes
        packet.recalc
        @pcap_file.body << get_pcap_packet(packet, next_ts(start_time, elapsed))
      end
      # Now bump up the timestamp to cover the gap
      timestamp += count * DTMFPayload::TIMESTAMP_INTERVAL
    else
    end
  end
  @pcap_file
end
empty?() click to toggle source
# File lib/sippy_cup/media.rb, line 28
def empty?
  @sequence.empty?
end
reset!() click to toggle source
# File lib/sippy_cup/media.rb, line 19
def reset!
  @sequence = []
end

Private Instance Methods

get_pcap_packet(packet, timestamp) click to toggle source
# File lib/sippy_cup/media.rb, line 102
def get_pcap_packet(packet, timestamp)
  PacketFu::PcapPacket.new :timestamp => timestamp,
                           :incl_len => packet.to_s.size,
                           :orig_len => packet.to_s.size,
                           :data => packet.to_s
end
get_step(input) click to toggle source
# File lib/sippy_cup/media.rb, line 94
def get_step(input)
  action, value = input.split ':'
  raise "Invalid Sequence: #{input}" unless VALID_STEPS.include? action

  [action, value]
end
new_packet() click to toggle source
# File lib/sippy_cup/media.rb, line 116
def new_packet
  packet = PacketFu::UDPPacket.new
  packet.ip_src = @from_addr.to_i
  packet.ip_dst = @to_addr.to_i
  packet.udp_src = @from_port
  packet.udp_dst = @to_port
  packet
end
next_ts(start_time, offset) click to toggle source
# File lib/sippy_cup/media.rb, line 109
def next_ts(start_time, offset)
  distance = offset * MSEC
  sec = start_time.to_i + (distance / USEC)
  usec = distance % USEC
  PacketFu::Timestamp.new(sec: sec, usec: usec).to_s
end