Initial commit

master
Nicolas Schoemaeker (nschoe) 11 years ago
commit c645efdb83

@ -0,0 +1,18 @@
---
title: About Me
---
My name is Nicolas SCHOEMAEKER.
I (currently) live in France.
This year, I am going to get a dual degree as an engineer in **Analog Electronics Integrated Design** with **Radio Frequency** major and a Master in Company Administration.
I used to design websites and now shifted my focus onto [Haskell](/haskell.html) and [Artificial Intelligence](/ai.html).
I play Squash regularly and have played Baskell-Ball for 11 years.
You can read my CV [here](/cv.pdf).
I go generally by the nickname "nschoe" on the Web.
You can take a look at my [Github](https://github.com/nschoe).
You can follow me on [Twitter](https://twitter.com/ns_schoe).
I used [Hakyll](http://jaspervdj.be/hakyll/) to power this website.

13
ai.md

@ -0,0 +1,13 @@
---
title: Artificial Intelligence
---
I am basically in love with Artificial Intelligence (Machine Learning, (Deep) Neural Networks, Support Vector Machines, etc).
Here are some very good references:
- [Artificial Intelligence, a Modern Approach](http://aima.cs.berkeley.edu/), a very good book about a lot of methods and algorithms, written by **[Stuart Russell](http://www.cs.berkeley.edu/~russell/)** and **[Peter Norvig](http://www.norvig.com/)**
- [An introduction course about Machine Learning](https://www.coursera.org/course/ml) by [Andrew Ng](https://twitter.com/AndrewYNg).
- The [Reddit Artificial Subreddit](https://reddit.com/r/artificial) contains interesting links.
I am currently reading and taking notes about the first book, and I will try to regularly post implementations (in Haskell) of some algorithms described in the book. I will try to update them regularly.

@ -0,0 +1,232 @@
/* CSS reseter */
html,body,p,div,h1,h2,h3,h4,h5,h6,img,article,section {
margin: 0;
padding: 0;
}
/* GENERAL */
html {
font-family: 'Source Sans Pro', sans-serif;
}
body {
position: absolute;
/*background: url('/images/background_blurred.jpg') no-repeat fixed;*/
background: #EEEEEE;
width: 100%;
/*min-height: 1500px; /*DEBUG*/
}
a {
text-decoration: none;
color: black;
border-bottom: 1px dashed black;
}
p {
text-align: justify;
font-size: 1.3em;
margin-bottom: 20px;
}
::selection {
background: rgba(0,0,0,0.3);
text-shadow: 1px 1px 1px white;
}
::-moz-selection {
background: rgba(0,0,0,0.3);
text-shadow: 1px 1px 1px white;
}
blockquote {
font-style: italic;
font-family: 'Dancing Script', cursive;
font-size: 1.2em;
opacity: 0.5;
}
code {
font-family: 'Inconsolata';
font-size: 0.8em;
}
code::-moz-selection {
background: white;
text-shadow: none;
}
li {
list-style-type: square;
}
/* HEADER */
header {
border: 1px solid black; /*DEBUG*/
position: fixed;
box-sizing: border-box;
width: 100%;
height: 60px;
background: linear-gradient(rgba(0,0,0,0.9), rgba(0,0,0,0.7));
border-bottom: 3px solid black;
}
header > a {
/*box-sizing: border-box;*/
display: inline-block;
height: 100%;
width: 120px;
text-align: center;
line-height: 60px;
text-transform: uppercase;
color: rgba(200,200,200,1);
transition: background 0.35s;
border: none;
}
header > a:hover:not([href$="rss.xml"]) {
background: rgba(0,0,0,1);
}
a[href$="rss.xml"] {
position: absolute;
right: 10px;
opacity: 0.7;
transition: opacity 0.5s;
}
a[href$="rss.xml"]:hover {
opacity: 1.0;
}
header img {
height: 100%;
}
/* CONTENT */
h1 {
text-transform: uppercase;
height: 60px;
line-height: 60px;
text-align: center;
/*background: linear-gradient(rgb(200,200,200), rgb(220,220,220));*/
background: rgb(245,245,245);
box-shadow: 0px 0px 2px 0px gray;
margin-bottom: 30px;
text-shadow: 1px 1px 2px white;
}
h2 {
text-align: center;
color: rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.2);
}
h3 {
color: rgba(0,0,0,0.7);
margin-top: 10px;
font-size: 1.5em;
}
h4 {
text-decoration: underline;
margin-top: 10px;
background: linear-gradient(90deg, rgba(0,0,0,0.1), rgba(0,0,0,0.0));
}
h4:before {
content: "> ";
}
#content {
margin-top: 70px;
width: 1200px;
margin-left: calc(50% - 600px);
}
#left_panel {
box-sizing: border-box;
display: inline-block;
vertical-align: top;
/*border: 1px solid red; /*DEBUG*/
width: 45%;
/*padding: 30px;*/
}
#last_articles, #last_messages, #last_tweets, #article_list {
background: rgb(245,245,245);
box-shadow: 0px 1px 1px rgba(0,0,0,0.2);
}
#last_articles h3 {
color: rgba(0,0,0,0.7);
background: url("/images/lambda.png") no-repeat;
background-size: contain;
padding-left: 20px;
}
#last_messages {
margin-top: 50px;
}
#last_messages h3 {
background: url("/images/envelope.png") no-repeat;
background-size: contain;
padding-left: 50px;
}
.recent_article, .article {
margin-top: 10px;
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 7px 10px;
}
.post_description {
font-style: italic;
color: rgba(0,0,0,0.6);
font-size: 0.9em;
transition: color 0.5s;
}
.post_description:hover {
color: rgba(0,0,0,0.9);
}
.date_article {
display: inline-block;
width: 100%;
text-align: right;
color: rgba(0,0,0,0.9);
font-size: 0.9em;
}
#right_panel {
box-sizing: border-box;
display: inline-block;
vertical-align: top;
/*border: 1px solid green; /*DEBUG*/
width: 45%;
margin-left: 115px;
text-align: right;
}
/* ARTICLES */
#article_list {
margin: 0;
padding: 0;
text-indent: 0px;
}
#article_list > li {
list-style-type: none;
}
#article_list a {
display: block;
border: none;
}

@ -0,0 +1,42 @@
/* Generated by pandoc. */
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre
{ margin: 0; padding: 0; border: 0; vertical-align: baseline; border: none; }
td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; }
td.sourceCode { padding-left: 5px; }
pre.sourceCode {
background: rgba(0,0,0,0.3);
width: 70%;
margin: 20px auto;
padding: 10px;
overflow: auto;
border: 1px solid black;
border-radius: 3px;
box-shadow: 1px 1px 1px 0px gray;
}
code {
border: 1px solid black;
background: rgba(0,0,0,0.3);
padding: 0px 5px;
}
pre.sourceCode span.kw { color: #A91700; font-weight: bold; }
pre.sourceCode span.dt { color: #9D2A2A; }
pre.sourceCode span.dv { color: #40a070; }
pre.sourceCode span.bn { color: #40a070; }
pre.sourceCode span.fl { color: #40a070; }
pre.sourceCode span.ch { color: #4070a0; }
pre.sourceCode span.st { color: #4070a0; }
pre.sourceCode span.co { color: #60a0b0; font-style: italic; }
pre.sourceCode span.ot { color: #504E4D; font-weight: bold }
pre.sourceCode span.al { color: red; font-weight: bold; }
pre.sourceCode span.fu { color: #06287e; }
pre.sourceCode span.re { }
pre.sourceCode span.er { color: red; font-weight: bold; }
pre.sourceCode code {
border: none;
background: none;
padding: 0;
}

BIN
cv.pdf

Binary file not shown.

@ -0,0 +1,67 @@
---
title: Haskell
---
### What is Haskell ?
[Haskell](https://www.haskell.org/haskellwiki/Haskell) is a _functional_ programming language with _strong static typing_. [Alp Mestan](http://www.alpmestan.com/) introduced me to Haskell some years ago and I have been a fan ever since.
### Why Haskell ?
There are several reasons why I love Haskell, let me tell you about some of them ; I will talk about ideas and concepts (things that happen in the mind) rather than programing language theory.
#### Lazyness
Haskell is a lazy language. What does it mean ?
It means that something (may it be a variable, a function, a list, anything) only gets evaluated when it is needed. Specifically, it means that you can deal with _infinity_. For instance, you can write
``` haskell
let xs = [1,2..]
```
which defines an infinite list. The program will compile **and** execute fine, meaning it will not hang. The reason for that is that we did not use the list, so Haskell did not evaluated it.
It gets even better! If you create an inifine list and display the first 10 elements, it will still compile and execute fine. This is because since we only needed the first 10 elements, Haskell only evaluated the first 10 elements, and not the rest of the list.
#### Functional
What does it mean that Haskell is functional ?
If you are looking for a complete definition, let me point you to the [Wikipedia page](https://en.wikipedia.org/wiki/Functional_programming). But basically, I tend to think about functional programming languages (and especially Haskell) roughly the same way I think about Mathematics. So basically, you can apply the same mental schemes in Haskell than you do in Maths.
In particular, in Haskell, functions are of _higher order_, meaning that they are on the same level than variables: you can pass a function as an argument to another function exactly the same way you would pass a variable.
Another very useful thing is that you can partially apply a function: by not supplying a function with all its parameters make it another function that you can pass around, and/or apply.
#### Recursivity
In Haskell, there is no `while` or `for` loop. Instead you make extensive use of recursion.
When I am asked to explain Haskell and the differences with non-functional programming Haskell (like in C or C++), this is what I say:
> In C or C++, you tell the computer how to **do** things, in Haskell, you tell the computer what things **are**.
To clarify things, imagine you want to compute the length of a list. In C, you would make a while loop, and increment a counter as long as you find elements in the list. In other worlds, in C you tell the computer to keep incrementing the counter as long as there are elements in the list.
In Haskell, you tell the computer that the length of an empty list is 0 and that the length of a non-empty list is 1 + the length of the list from which you removed the first element (called the head).
It turns out this way of describing programs really fits with how my mind works, and I love it! This seems much more natural, and I like the proximity with Maths (for those who wonder, there is lambda calculus in Haskell).
#### Compacity
Haskell is a very compact language. You can achieve complex operations in only a few lines of code. This is thanks to function currying and the fact that funtions are of higher order.
Besides, those complex operations are very easy to understand because of the way functions are chained. It is (again) similar to Mathematics with the functions compositions operator.
A simple example: here is a way to count the number of words in a file, in Haksell.
``` haskell
readFile "/path/to/file" >>= return . length . words
```
That is literally **all**. It works, just like that. In basically one line of Haskell code, you can scan a file, count the number of worlds in it. Pretty neat. Look at how functions are chained together, passing the result from one function as a the input for the next one, and so on. Here,
- `readFile` is a function whose argument is `"path/to/file"`
- the result of this (the content of the file) is passed to the `words` function, which cuts a string into a list of strings (words)
- this list of words is passed to the function `length` which returns the length of the list (number of elements)
- This result is then passed to the function `return` (yes, this is Haskell-specific : `return` is just like any other function, not a keyword, as it is in C).
Another example, suppose we want to compute the number of times the word "the" appear in a file, again, **one** line of Haskell solves this:
``` haskell
readFile "/path/to/file" >>= return . length . filter ((==) "the") . words
```
And here you are. Anyway, I'm just trying to show that Haskell makes extensive use of function combinaison, and that it reads pretty well, and this is why I love Haskell.
### How / Where to Learn Haskell ?
You will find a lot of ressources on the Web, so I'll be brief.
- There is the excellent [Real World Haskell](http://book.realworldhaskell.org/read/) which you can read online.
- [Learn You a Haskell For Great Good](http://learnyouahaskell.com/) is a valuable piece of information.
- I have to point to the **#haskell** IRC channel. The community is among the nicest and most enthusiast there is. Here you can ask questions and you will be answered, quite rapidly. This is much appreciated!
- Another useful ressource is [Hoogle](https://www.haskell.org/hoogle/) which is, as the name implies, "Google for Haskell". In that search engine, you can search functions by type, by module, etc.
Besides, Haskell is very well supported by Emacs and Sublime Text, you can have nice and handy integration (syntaxt highlighting of course, auto-completion, documentation, type signature, hoogle search, etc)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -0,0 +1,57 @@
---
title: Welcome Home !
---
<section id="left_panel">
<div id="last_articles">
<h2>Last Articles</h2>
$for(lastTwoArticles)$
<div class="recent_article">
<h3>$title$</h3>
<p class="post_description">
$description$
<br>
<span class="date_article">$date$</span>
</p>
</div>
$endfor$
</div>
<div id="last_messages">
<h2>Last Messages</h2>
$for(lastTwoMessages)$
<div class="recent_article">
<h3>$title$</h3>
<p class="post_description">
$body$
<br>
<span class="date_article">$date$</span>
</p>
</div>
$endfor$
</div>
</section>
<section id="right_panel">
<div id="last_tweets">
<a class="twitter-timeline"
href="https://twitter.com/ns_schoe"
data-widget-id="545190988923568128"
data-chrome="transparent nofooter">
Tweets by @ns_schoe
</a>
<script async>
!function(d,s,id)
{
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
if (!d.getElementById(id))
{
js=d.createElement(s);
js.id=id;
js.src=p+"://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js,fjs);
}
}(document,"script","twitter-wjs");
</script>
</div>
</section>

@ -0,0 +1,11 @@
name: nschoeslabs
version: 0.1.0.0
build-type: Simple
cabal-version: >= 1.10
executable site
main-is: site.hs
build-depends: base == 4.*
, hakyll == 4.6.*
ghc-options: -threaded
default-language: Haskell2010

@ -0,0 +1,100 @@
--------------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid (mappend, (<>))
import Hakyll
--------------------------------------------------------------------------------
main :: IO ()
main = hakyll $ do
match "images/*" $ do
route idRoute
compile copyFileCompiler
match "css/*" $ do
route idRoute
compile compressCssCompiler
match (fromList ["about.md", "contact.markdown", "haskell.md", "ai.md", "webrtc.md"]) $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
match "articles/*" $ do
route $ setExtension "html"
compile $ pandocCompiler
>>= loadAndApplyTemplate "templates/article.html" articleCtx
>>= loadAndApplyTemplate "templates/default.html" articleCtx
>>= relativizeUrls
match "cv.pdf" $ do
route idRoute
compile copyFileCompiler
create ["articles.html"] $ do
route idRoute
compile $ do
articles <- recentFirst =<< loadAll "articles/*"
let articlesCtx =
listField "articles" articleCtx (return articles) <>
constField "title" "Articles" <>
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/articles.html" articlesCtx
>>= loadAndApplyTemplate "templates/default.html" articlesCtx
>>= relativizeUrls
match "messages/*" $ do
route $ setExtension "html"
compile $ getResourceBody
>>= relativizeUrls
-- create ["archive.html"] $ do
-- route idRoute
-- compile $ do
-- articles <- recentFirst =<< loadAll "articles/*"
-- let archiveCtx =
-- listField "articles" articlesCtx (return articles) `mappend`
-- constField "title" "Archives" `mappend`
-- defaultContext
-- makeItem ""
-- >>= loadAndApplyTemplate "templates/archive.html" archiveCtx
-- >>= loadAndApplyTemplate "templates/default.html" archiveCtx
-- >>= relativizeUrls
match "index.html" $ do
route idRoute
compile $ do
lastTwoArticles <- return . take 2 =<< recentFirst =<< loadAll "articles/*"
lastTwoMessages <- return . take 2 =<< recentFirst =<< loadAll "messages/*"
let indexCtx =
listField "lastTwoArticles" articleCtx (return lastTwoArticles) <>
listField "lastTwoMessages" articleCtx (return lastTwoMessages) <>
defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= relativizeUrls
match "templates/*" $ compile templateCompiler
--------------------------------------------------------------------------------
articleCtx :: Context String
articleCtx =
dateField "date" "%B %e, %Y" `mappend`
defaultContext
-- lastTwoArticlesCtx :: Context String
-- lastTwoArticlesCtx =
-- dateField "date" "%b %e, %Y" `mappend`
-- defaultContext
-- lastMessagesCtx :: Context String
-- lastMessagesCtx =
-- dateField "date" "%b %e, %Y" `mappend`
-- defaultContext

@ -0,0 +1,5 @@
<div id="article_content">
$body$
<br>
<span class="date_article">$date$</span>
</div>

@ -0,0 +1,18 @@
Here is what I wrote so far, hope you find something of interest.<br>
If you have any suggestions, please <a href="contact.html">contact me</a>.
<ul id="article_list">
$for(articles)$
<li>
<a href="$url$">
<div class="article">
<h3>$title$</h3>
<p class="post_description">$description$
<br>
<span class="date_article">$date$</span>
</p>
</div>
</a>
</li>
$endfor$
</ul>

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta author="Nicolas SCHOEMAEKER (nschoe) <ns.schoe _at_ gmail _dot_ com">
<meta description="My place on the Internet to share and explain what I know">
<title>nschoe's labs - $title$</title>
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Dancing+Script' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="/css/default.css">
<link rel="stylesheet" type="text/css" href="/css/syntax_highlighting.css">
</head>
<body>
<header>
<a href="/">nschoe's labs</a>
<a href="/">Home</a>
<a href="/articles.html">Articles</a>
<a href="/haskell.html">Haskell</a>
<a href="/ai.html">A.I.</a>
<a href="/webrtc.html">WebRTC</a>
<a href="/about.html">About</a>
<a href="mailto:ns.schoe@gmail.com">Contact</a>
<a href="/rss.xml"><img src="/images/rss.png" alt="RSS feed logo"></a>
</header>
<div id="content">
<h1>$title$</h1>
$body$
<!-- <footer>
Site proudly generated by
<a href="http://jaspervdj.be/hakyll">Hakyll</a>
</footer> -->
</div>
</body>
</html>

@ -0,0 +1,59 @@
---
title: WebRTC
---
### WebRTC in a nutshell
WebRTC signifies _Real Time Communication_ for Web technologies. WebRTC is the answer to the expansion of digital communication and the rise of everyone being connected.
I believe WebRTC will play a very important role in the IoT, even though it is not yet visible.
### WebRTC Forces
In this world where everyone talks about the "Cloud" and centralization of data, WebRTC is quite innovative.
The key and main points of WebRTC are :
#### Peer-to-Peer
This is what I was talking when I said WebRTC was innovative with respect to the Cloud, _for once_ we have something that doesn't transit through a server...
In WebRTC, you can make calls between peers, directly. Note this: WebRTC is **peer**-to-**peer**, _NOT_ **browser**-to-**browser**. This is hard to keep in mind because for now, the only easy and useful APIs are mainly written in Javascript, for web browsers. WebRTC evolves rapidly though, and the trend may change quicker than I expect.
#### Multimedia
The second true power of WebRTC is that it deals with multimedia streams. I am not merely saying that you can use multimedia with WebRTC (that would not make it much more interesting than other technologies), I am saying that WebRTC actually _deals_ (very well) with video and/or audio.
In web browsers for instance, WebRTC does [**Adaptive Bitrate Streaming**](https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming) whenever you are making a call (may it be audio or video). It basically means that it will take care of measuring the quality of the call, and when the bitrate fluctuates, instead of lagging or cutting the conversation, WebRTC will decrease the quality of the encoding to keep your call alive.
Another great feature that makes WebRTC extremely handy is that it performs automatic audio / video synchronization, preventing the quite unfortunate delay between the movement of your mouth and the actualy sound. I'm sure you know what I mean if you ever watched a movie in (bad) streaming.
#### Raw Data Too !
When you browse websites that talk about WebRTC, you always see audio, video or both. This is great indeed. But they always seem to neglect the third "channel" (WebRTC name): raw data.
WebRTC allows you to open a UDP channel between the two peers, to transmit raw data. And for me, this is the most awesome part: you can just define your own protocol (or use JSON, XML, whatever you want) and transmit, real time, anything you need.
- It can be a file, this way you can share a file peer-to-peer (you don't always want to host your file to an unknown server). How many times did you wonder how to send photos from your latest holidays, now WebRTC made it easy.
- It can be ascii strings, maybe you are implementing a streaming service and you want to provide the user with the subtitles.
- It can be any other raw data, maybe you are are implementing a multimedia video game and you transmit the positions of the players (or any other related data for all that matters).
Anyway, the possibilites are infinite, and maybe you can see why data channel are much more interesting than audio/video.
#### Secured
Ah! Now the real thing. WebRTC _ensures_ that your connections are secured. And by ensure, I mean that it is **mandatory**. You cannot have WebRTC connections happening in plaintext.
And in this world where robbers are now digital hackers or big companies use targeted ads, this sounds good.
### Basic Principle
I won't go into much details here as I'll try to write posts that explain better, but I'm going to introduce you to the basic principles behind WebRTC.
#### Signalling Channel
Lots of things happen in WebRTC between two peers: they need to negociate encryption settings, encoding settings, multimedia settings (do we transmit audio, video, data ?), etc.
In order for these parameters to be negociated, the two peers need to communicate with each other.
WebRTC makes use of a _signalling channel_ to transfer this information. Signalling channel is basically a "classic" channel between a peer and a central server. So yes, to implement WebRTC, you need a central server to initiate the calls. The peers use this signalling channel to negociate all the parameters they need and then start the conversation peer-to-peer, forgetting the central server.
#### SRTP
Underneath the UDP protocol, WebRTC makes use of the [Secure Real-time Transport Protocol](https://en.wikipedia.org/wiki/Secure_Real-time_Transport_Protocol). Basically, the data is sent via packets, which contain headers to give information. In particular (for audio and video), the timestamp at which the sample was captured is saved, so that the receiver can perform jitter protection and audio / video synchronization.
The header contains a _Payload Type (PT)_ which describes the kind of data it transports.
#### SDP
To negociate the parameters, the peers need to use a common format. [Session Description Protocol (SDP)](https://en.wikipedia.org/wiki/Session_Description_Protocol) is used in case of WebRTC. There is currently lots of discussion as wether JSON should be used too, but at the moment, SDP is chosen. Basically, SDP contains information about the IP address of the peer, the payload type, the bitrate used, etc.
#### STUN server
In order for peers to communicate directly, they need to tell each other (among other things) their IP addresses. But today, personal computers are all hidden behing NAT (router, Internet Box, etc). So they don't even know their IP address. [Session Traversal Utilities for NAT (STUN)](https://en.wikipedia.org/wiki/STUN) is a server used for peer to discover their own IP address, exactly the same way you do when you open your browser and go to a website to know your IP address.
#### TURN servers
For some reasons sometimes, peer-to-peer connection fails to establish (Google's stats say something aroun 8%). In these cases, WebRTC is not possible directly, you have to use [Traversal Using Relays around NAT (TURN)](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) servers, this is basically a server which acts as a relay between the two peers.
It removes the peer-to-peer aspect of WebRTC connections, since it now goes through a relay server, so you have to be careful.
This is all for WebRTC presentation, I will try to write detailed posts about the differents aspects and show you some implementations.
Loading…
Cancel
Save