PalavaMachine – WebRTC Signaling powered by Ruby – Jan Lelis | Marius Melzer



PalavaMachine – WebRTC Signaling powered by Ruby – Jan Lelis | Marius Melzer

0 1


talk-rugb-palava-machine

PalavaMachine: WebRTC Signaling powered by Ruby (rugb / October 10)

On Github palavatv / talk-rugb-palava-machine

PalavaMachine

WebRTC Signaling powered by Ruby

Ruby User Group Berlin | October 10, 2013

Jan Lelis | Marius Melzer

@happycode | @faraoso

What is WebRTC?

What is palava?

What is Signaling?

What is PalavaMachine?

Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).

Upcoming Standard

Internet Engineering Task Force

World Wide Web Consortium

getUserMedia

<video id="gum-video" autoplay="autoplay">

<script>
  navigator.webkitGetUserMedia(
    {video: true, audio: false},
    function(stream) {
      document.getElementById('gum-video').src =
          webkitURL.createObjectURL(stream);
    }
  );
</script>
            </video>

Signaling

PalavaMachine

PalavaMachine

Signaling server written in Ruby

Talks WebSockets via em-websockets

Allows client to join a room to find other clients

Signaling Protocol

A few defined JSON messages

Client → Server

join_room(room_id, status)

send_to_peer(peer_id, data)

update_status(status)

Server → Client

joined_room(me, peers)

peer_updated_status(peer_id, peer_status)

error(id, message)

shutdown(seconds)

Client → Client

via send_to_peer

offer(sdp)

answer(sdp)

ice_candidate(...)

EventMachine Reactor

EM.run{
  em_init
  trap(:TERM){ em_sigterm }
  trap(:INT){ em_sigint }

  EM::WebSocket.run(options){ |ws|
    ws.onopen{ |handshake|  ws_open(ws, handshake) }
    ws.onmessage{ |message| ws_message(ws, message) }
    ws.onclose{ |why|       ws_close(ws, why) }
    ws.onerror{ |error|     ws_error(ws, error) }
    EM.error_handler{ |e|   em_error(e) }
  }
}
def ws_message(ws, message)
  ws_message_action(ws, ws_message_parse(ws, message))
rescue MessageParsingError, MessageError => e
  send_error(ws, e)
end
def ws_message_parse(ws, message)
  connection_id = manager.connections.get_connection_id(ws) or
      raise MessageError.new(ws), 'unknown connection'
  ClientMessage.new(message, connection_id)
end
def ws_message_action(ws, message_event)
  manager.debug "#{message_event.connection_id} <#{message_event.name}>"
  manager.public_send(
    message_event.name,
    message_event.connection_id,
    *message_event.arguments
  )
end

Redis Store

Redis Store

Stores in which room a connection resides

Stores a connection ids per open socket

Uses asynchronous em-hiredis driver

Join Room Example / Ruby Land

def join_room(connection_id, room_id, status)
  return_error connection_id, 'no room id given' if !room_id
  return_error connection_id, 'room id too long' if room_id.size > 50
  # ...
  info "#{connection_id} joins ##{room_id[0..10]}... #{status}"
  script_join_room(connection_id, room_id, status){ |members|
    # ...
def script_join_room(connection_id, room_id, status, &block)
  @redis.eval \
    SCRIPT_JOIN_ROOM,
    4,
    "store:room:members:#{room_id}",
    "store:room:peak_members:#{room_id}",
    "store:connection:joined:#{connection_id}",
    "store:connection:room:#{connection_id}",
    connection_id,
    PAYLOAD_NEW_PEER[connection_id, status],
    Time.now.getutc.to_i,
    room_id,
    &block
end

Join Room Example / Lua Land

local members = redis.call('smembers', KEYS[1])
local count = 0
for _, peer_id in pairs(members) do
  redis.call('publish', "ps:connection:" .. peer_id, ARGV[2])
  count = count + 1
end
redis.call('sadd', KEYS[1], ARGV[1])
if count == 0 or tonumber(redis.call('get', KEYS[2])) <= count then
  redis.call('set', KEYS[2], count + 1)
end
redis.call('set', KEYS[3], ARGV[3])
redis.call('set', KEYS[4], ARGV[4])
return members

Inter Machine Communication

Inter Machine Communication

Using redis' PubSub feature

Allows for multiple EM socket servers running simultanously

Every socket connection listens on its own redis queue

Redis PubSub

def announce_connection(ws)
  connection_id = @connections.register_connection(ws)
  info "#{connection_id} <open>"

  @subscriber.subscribe "ps:connection:#{connection_id}" do |payload|
    ws.send_text(payload)
  end
end
# ...

unless %w[offer answer ice_candidate].include? data['event']
  return_error connection_id, 'event not allowed'
end

@publisher.publish "ps:connection:#{peer_id}",
    (data || {}).merge("sender_id" => connection_id).to_json

Undocumented Bonus Feauters

Extendable Plugin Architecture

General Usage Stats

Palava Demo

https://palava.tv

Alternatives

All written in node.js

signalmaster

webrtc.io

together.js hub

peerjs server

Palava as a non-profit organization

Thank you | Find the Code at

github.com/palavatv/palava-machine

One more thing

signaling.io

We are working on a webrtc signaling service. You might want to check it out if you consider using webrtc in a future project.

Thank you | Find the Code at

github.com/palavatv/palava-machine

More palava Information

@palavatv | blog.palava.tv

Signaling Service

http://signaling.io