From c1a909250505df06501744fc2121713a7f0d168f Mon Sep 17 00:00:00 2001 From: Daniel Berteaud Date: Fri, 20 Feb 2015 14:47:10 +0100 Subject: [PATCH] Get SimpleWebRTC conf from the API Instead of printing it in a JS scriplet inline. Also add a modal popup while the initial connection is being established Also make the max frame rate to be configured --- conf/settings.ini.dist | 5 +++ lib/Vroom/Constants.pm | 5 +-- lib/Vroom/I18N/en.pm | 2 ++ lib/Vroom/I18N/fr.pm | 2 ++ public/img/connecting.gif | Bin 0 -> 9793 bytes public/js/vroom.js | 38 ++++++++++++++++++--- templates/default/join.html.ep | 73 +++++++++++++---------------------------- vroom.pl | 49 +++++++++++++++++++++++++++ 8 files changed, 117 insertions(+), 57 deletions(-) create mode 100644 public/img/connecting.gif diff --git a/conf/settings.ini.dist b/conf/settings.ini.dist index 485ae1e..7d0a3e2 100644 --- a/conf/settings.ini.dist +++ b/conf/settings.ini.dist @@ -20,6 +20,11 @@ ; the realm used for turn accounts. Must match the realm of your turn server ;realm = 'vroom' +[video] +; Define the max frame rate for video +; higher will produce better quality streams, but will also require more bandwidth and CPU power +;frame_rate = 15 + [email] ; Address set in the From field of email sent by VROOM ;from = 'no-reply@example.com' diff --git a/lib/Vroom/Constants.pm b/lib/Vroom/Constants.pm index cd6b13f..8dd8234 100644 --- a/lib/Vroom/Constants.pm +++ b/lib/Vroom/Constants.pm @@ -122,10 +122,11 @@ use constant API_ACTIONS => { get_room_conf => 1, get_peer_role => 1, join => 1, - get_padd_session => 1 + get_padd_session => 1, + get_rtc_conf => 1 }, anonymous => { - create_room => 1 + create_room => 1 } }; diff --git a/lib/Vroom/I18N/en.pm b/lib/Vroom/I18N/en.pm index 813f099..015047c 100644 --- a/lib/Vroom/I18N/en.pm +++ b/lib/Vroom/I18N/en.pm @@ -229,6 +229,8 @@ our %Lexicon = ( "CLICK_IF_NO_WEBCAM" => "If you don't have a webcam, click the following link, you'll be able to join the room with audio only", "CONNECTION_LOST" => "You have been disconnected", "CHECK_INTERNET_ACCESS" => "Please, check your Internet connection, then refresh this page", + "CONNECTING" => "Connecting", + "CONNECTING_PLEASE_WAIT" => "Please wait while establishing the connection", "HOME" => "Home", "HELP" => "Help", "ABOUT" => "About", diff --git a/lib/Vroom/I18N/fr.pm b/lib/Vroom/I18N/fr.pm index a45c954..1100c2e 100644 --- a/lib/Vroom/I18N/fr.pm +++ b/lib/Vroom/I18N/fr.pm @@ -238,6 +238,8 @@ our %Lexicon = ( "en audio uniquement", "CONNECTION_LOST" => "Vous avez été déconnecté", "CHECK_INTERNET_ACCESS" => "Vérifiez votre connexion, et raffraîchissez cette page ensuite", + "CONNECTING" => "Connexion en cours", + "CONNECTING_PLEASE_WAIT" => "Veuillez patienter le temps d'établir la connexion", "HOME" => "Accueil", "HELP" => "Aide", "ABOUT" => "À propos", diff --git a/public/img/connecting.gif b/public/img/connecting.gif new file mode 100644 index 0000000000000000000000000000000000000000..c3e9d00ba6cf857da13b7adec2697ccb54a85b84 GIT binary patch literal 9793 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 N-)Z;VzeVxx{{o4HW_tht literal 0 HcmV?d00001 diff --git a/public/js/vroom.js b/public/js/vroom.js index e2bd0cb..4461585 100644 --- a/public/js/vroom.js +++ b/public/js/vroom.js @@ -21,6 +21,9 @@ var locale = {}; // When pagination is done, how many item per page var itemPerPage = 20; +// Will store the global webrtc object +var webrtc = undefined; + // Localize the strings we need $.ajax({ url: rootUrl + 'localize/' + currentLang, @@ -575,6 +578,30 @@ function initAdmin(){ }); } +function initJoin(room){ + $.ajax({ + data: { + req: JSON.stringify({ + action: 'get_rtc_conf', + param: { + room: room, + } + }) + }, + error: function(data){ + showApiError(data); + }, + success: function(data){ + if (!video){ + data.config.media.video = false; + } + data.config.localVideoEl = 'webRTCVideoLocal'; + webrtc = new SimpleWebRTC(data.config); + initVroom(room); + } + }); +} + // This is the main function called when you join a room function initVroom(room) { @@ -876,7 +903,7 @@ function initVroom(room) { // color can be sent through the signaling channel peer.send('peer_color', {color: peers.local.color}); // if we don't have a video, just signal it to this peer - peer.send('media_info', {video: !!videoConstraints}); + peer.send('media_info', {video: !!video}); // We don't have chat history yet ? Lets ask to this new peer if(!peers.local.hasHistory && chatIndex == 0){ peer.sendDirectly('vroom', 'getHistory', ''); @@ -1515,11 +1542,14 @@ function initVroom(room) { if (data.msg){ $.notify(data.msg, 'success'); } + $('#videoLocalContainer').show(200); + $('#timeCounterXs,#timeCounter').tinyTimer({ from: new Date }); + setTimeout(function(){ + $('#connecting').modal('hide'); + }, 200); } }); checkMoh(); - $('#videoLocalContainer').show(200); - $('#timeCounterXs,#timeCounter').tinyTimer({ from: new Date }); }); // Handle new video stream added: someone joined the room @@ -1764,7 +1794,7 @@ function initVroom(room) { }); // Disable suspend webcam button if no webcam - if (!videoConstraints){ + if (!video){ $('.btn-suspend-cam').addClass('disabled'); } diff --git a/templates/default/join.html.ep b/templates/default/join.html.ep index ce2f63e..e758369 100644 --- a/templates/default/join.html.ep +++ b/templates/default/join.html.ep @@ -377,6 +377,24 @@ + %=include 'invite_modal' %=include 'configure_modal' %=include 'auth_modal' @@ -432,7 +450,6 @@ %= include 'footer' diff --git a/vroom.pl b/vroom.pl index e94213f..b951c4a 100755 --- a/vroom.pl +++ b/vroom.pl @@ -36,6 +36,7 @@ $config->{'turn.turn_server'} ||= undef; $config->{'turn.turn_user'} ||= undef; $config->{'turn.turn_password'} ||= undef; $config->{'turn.realm'} ||= 'vroom'; +$config->{'video.frame_rate'} ||= 15; $config->{'email.from '} ||= 'vroom@example.com'; $config->{'email.contact'} ||= 'admin@example.com'; $config->{'email.sendmail'} ||= '/sbin/sendmail'; @@ -1262,6 +1263,7 @@ any '/api' => sub { } ); } + # And here anonymous method, which do not require an API Key elsif ($req->{action} eq 'create_room'){ $req->{param}->{room} ||= $self->get_random_name(); $req->{param}->{room} = lc $req->{param}->{room}; @@ -1601,6 +1603,53 @@ any '/api' => sub { status => 403 ); } + # Return configuration for SimpleWebRTC + elsif ($req->{action} eq 'get_rtc_conf'){ + my $resp = { + url => $config->{'signaling.uri'}, + peerConnectionConfig => { + iceServers => [{ + url => $config->{'turn.stun_server'} + }] + }, + autoRequestMedia => Mojo::JSON::true, + enableDataChannels => Mojo::JSON::true, + debug => Mojo::JSON::false, + detectSpeakingEvents => Mojo::JSON::true, + adjustPeerVolume => Mojo::JSON::false, + autoAdjustMic => Mojo::JSON::false, + harkOptions => { + interval => 300, + threshold => -20 + }, + media => { + audio => Mojo::JSON::true, + video => { + mandatory => { + maxFrameRate => $config->{'video.frame_rate'} + } + } + } + }; + # TODO: Support several TURN server in config + if ($config->{'turn.turn_server'}){ + my $turn = { url => $config->{'turn.turn_server'} }; + if ($config->{'turn.turn_user'} && $config->{'turn.turn_password'}){ + $turn->{username} = $config->{'turn.turn_user'}; + $turn->{credential} = $config->{'turn.turn_password'}; + } + else{ + $turn->{username} = $room->{name}; + $turn->{credential} = $room->{token}; + } + push @{$resp->{peerConnectionConfig}->{iceServers}}, $turn; + } + return $self->render( + json => { + config => $resp + } + ); + } # Return just room config elsif ($req->{action} eq 'get_room_conf'){ return $self->render(