tag:blogger.com,1999:blog-54177858528494254492024-03-06T05:22:13.276+05:00Muaz Khan BlogProgramming (php, aspnet, python, c#, java, javascript and WebRTC!)Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comBlogger22125tag:blogger.com,1999:blog-5417785852849425449.post-61505281104339261022017-08-25T09:10:00.000+05:002017-09-04T12:22:27.048+05:00Write Screen Sharing Application for Your Own WebSite<div dir="ltr" style="text-align: left;" trbidi="on">
Do you want to write multi-user screen sharing application for your own website?<br />
<br />
Note: This tutorial uses WebRTC for multi-user screen sharing.<br />
<br />
<b>First step</b>, download all codes from this directory: <a href="https://github.com/muaz-khan/Chrome-Extensions/tree/master/desktopCapture" target="_blank">github/Chrome-Extensions/desktopCapture</a><br />
<br />
<b>Second step</b>, modify manifest.json file <a href="https://github.com/muaz-khan/Chrome-Extensions/blob/master/desktopCapture/manifest.json#L17" target="_blank">line 17</a>, and replace with either localhost or your real domain name:<br />
<br />
<pre style="background: #fdf6e3; border-radius: 7px; color: #586e75; padding: 5px 10px;">{
<span style="color: #269186;"><span style="color: #c60000;">"</span>content_scripts<span style="color: #c60000;">"</span></span>: <span style="color: #d31e1e;">[</span> {
<span style="color: #269186;"><span style="color: #c60000;">"</span>js<span style="color: #c60000;">"</span></span>: <span style="color: #d31e1e;">[</span> <span style="color: #269186;"><span style="color: #c60000;">"</span>content-script.js<span style="color: #c60000;">"</span></span> <span style="color: #d31e1e;">]</span>,
<span style="color: #269186;"><span style="color: #c60000;">"</span>all_frames<span style="color: #c60000;">"</span></span>: <span style="color: #b58900;">true</span>,
<span style="color: #269186;"><span style="color: #c60000;">"</span>run_at<span style="color: #c60000;">"</span></span>: <span style="color: #269186;"><span style="color: #c60000;">"</span>document_end<span style="color: #c60000;">"</span></span>,
<span style="color: #269186;"><span style="color: #c60000;">"</span>matches<span style="color: #c60000;">"</span></span>: <span style="color: #d31e1e;">[</span><span style="color: #269186;"><span style="color: #c60000;">"</span>https://your-domain.com/*<span style="color: #c60000;">"</span></span><span style="color: #d31e1e;">]</span>
}<span style="color: #d31e1e;">]</span>
}
</pre>
<br />
<b>Fourth step</b>, either max a zip and deploy new chrome extension to <a href="https://chrome.google.com/webstore/developer/dashboard" target="_blank">Google App Store,</a> or install directly using "chrome://extensions" page.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoQ6l3DKd0HLXvhZZzePXua1rgafUoJkmrvFFrIHzSgfbh6IP3Ma7CL6bxxN9NBtjFbhYksSTWMmF6Bbs0WX1aE4pU6PWl5Sdv9ln_dwu6vVVFeD-4Yv9QX6o2DFVV8ilCTvZnhOS2KkDw/s1600/load-unpacked-extension.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="364" data-original-width="835" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoQ6l3DKd0HLXvhZZzePXua1rgafUoJkmrvFFrIHzSgfbh6IP3Ma7CL6bxxN9NBtjFbhYksSTWMmF6Bbs0WX1aE4pU6PWl5Sdv9ln_dwu6vVVFeD-4Yv9QX6o2DFVV8ilCTvZnhOS2KkDw/s1600/load-unpacked-extension.png" style="max-width: 80%;" /></a></div>
<b><br /></b>
<span id="goog_1240718799"></span><span id="goog_1240718800"></span><b>Fifth step</b>, download ONE_to_MANY screen sharing demo from this page: <a href="https://github.com/muaz-khan/RTCMultiConnection/blob/master/demos/screen-sharing.html" target="_blank">RTCMultiConnection/demos/Screen-Sharing.html</a><br />
<br />
<b><br /></b>
<b>Sixth step</b>, modify a few lines in the above demo:<br />
<br />
Search for these two lines:<br />
<br />
<pre style="background: #fdf6e3; border-radius: 7px; color: #586e75; padding: 5px 10px;"><span style="color: #93a1a1;"><</span><span style="color: #268bd2; font-weight: 700;">script</span> <span style="color: #93a1a1;">src</span>=<span style="color: #269186;"><span style="color: #269186;">"</span>/dist/RTCMultiConnection.min.js<span style="color: #269186;">"</span></span><span style="color: #93a1a1;">></</span><span style="color: #268bd2; font-weight: 700;">script</span><span style="color: #93a1a1;">></span>
<span style="color: #93a1a1;"><</span><span style="color: #268bd2; font-weight: 700;">script</span> <span style="color: #93a1a1;">src</span>=<span style="color: #269186;"><span style="color: #269186;">"</span>/socket.io/socket.io.js<span style="color: #269186;">"</span></span><span style="color: #93a1a1;">></</span><span style="color: #268bd2; font-weight: 700;">script</span><span style="color: #93a1a1;">></span>
</pre>
<br />
Replace above two lines with:<br />
<br />
<pre style="background: #fdf6e3; border-radius: 7px; color: #586e75; padding: 5px 10px;"><span style="color: #93a1a1;"><</span><span style="color: #268bd2; font-weight: 700;">script</span> <span style="color: #93a1a1;">src</span>=<span style="color: #269186;"><span style="color: #269186;">"</span>https://rtcmulticonnection.herokuapp.com//dist/RTCMultiConnection.min.js<span style="color: #269186;">"</span></span><span style="color: #93a1a1;">></</span><span style="color: #268bd2; font-weight: 700;">script</span><span style="color: #93a1a1;">></span>
<span style="color: #93a1a1;"><</span><span style="color: #268bd2; font-weight: 700;">script</span> <span style="color: #93a1a1;">src</span>=<span style="color: #269186;"><span style="color: #269186;">"</span>https://rtcmulticonnection.herokuapp.com//socket.io/socket.io.js<span style="color: #269186;">"</span></span><span style="color: #93a1a1;">></</span><span style="color: #268bd2; font-weight: 700;">script</span><span style="color: #93a1a1;">></span>
</pre>
<br />
Now search for this line:<br />
<br />
<pre style="background: #fdf6e3; border-radius: 7px; color: #586e75; padding: 5px 10px;">connection.socketURL <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>/<span style="color: #c60000;">'</span></span>;
</pre>
<br />
Replace above line with:<br />
<br />
<pre style="background: #fdf6e3; border-radius: 7px; color: #586e75; padding: 5px 10px;">connection.socketURL <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>https://rtcmulticonnection.herokuapp.com:443/<span style="color: #c60000;">'</span></span>;
</pre>
<br />
Now you are done.<br />
<br />
<b>Seventh and the last step</b>, upload above HTML file on any HTTPs domain.<br />
<br />
You can try on LocalHost as well.<br />
<br />
Non-LocalHost domains requires HTTPs.<br />
<br />
You can upload to Google Drive, DropBox or any free hosting website that supports HTTPs e.g. appspot.com.<br />
<br />
<br />
<h3 style="text-align: left;">
How to test screen sharing?</h3>
First person shares his screen and about 10 people can join/see/view his screen.<br />
<br />
You can open unlimited parallel rooms.<br />
<br />
Maximum number of people who can view single screen is 14. We do not support more than 14 viewers/receivers.<br />
<br />
There is a separate demo that allows us share screen over more than 14 users: <a href="https://rtcmulticonnection.herokuapp.com/demos/Scalable-Screen-Broadcast.html">https://rtcmulticonnection.herokuapp.com/demos/Scalable-Screen-Broadcast.html</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-8861208668598278102017-08-20T10:07:00.000+05:002018-07-15T11:20:57.101+05:00RecordRTC and Upload to PHP Server<div dir="ltr" style="text-align: left;" trbidi="on">
This tutorial explains how to record videos and microphones using RecordRTC; and upload recorded data to your PHP server.<br />
<br />
<b>First step</b>, write a simple RecordRTC video recording application.<br />
<br />
Hope you didn't miss this youtube tutorial: <a href="https://www.youtube.com/watch?v=YrLzTgdJ-Kg">https://www.youtube.com/watch?v=YrLzTgdJ-Kg</a><br />
<br />
Here is a LIVE jsFiddle demo: <a href="https://jsfiddle.net/e32jwtn5/">https://jsfiddle.net/e32jwtn5/</a><br />
<br />
Source codes for the demo:<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><span style="color: #ac885b;"><!<span style="color: #494949;"><span style="color: #494949;">DOCTYPE</span> html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">head</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">src</span>=<span style="color: #8f9d6a;">"https://cdn.WebRTC-Experiment.com/RecordRTC.js"</span>></<span style="color: #e0c589;">script</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">head</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">video</span> <span style="color: #ac885b;">id</span>=<span style="color: #8f9d6a;">"your-video-id"</span> <span style="color: #ac885b;">controls</span>=<span style="color: #8f9d6a;">""</span> <span style="color: #ac885b;">autoplay</span>=<span style="color: #8f9d6a;">""</span>></<span style="color: #ac885b;">video</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">type</span>=<span style="color: #8f9d6a;">"text/javascript"</span>>
<span style="color: #5f5a60; font-style: italic;">// capture camera and/or microphone</span>
<span style="color: #9b859d;">navigator</span>.mediaDevices.getUserMedia({ video: <span style="color: #cf6a4c;">true</span>, audio: <span style="color: #cf6a4c;">true</span> }).then(<span style="color: #f9ee98;">function</span>(camera) {
<span style="color: #5f5a60; font-style: italic;">// preview camera during recording</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).muted <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">true</span>;
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> camera;
<span style="color: #5f5a60; font-style: italic;">// recording configuration/hints/parameters</span>
<span style="color: #f9ee98;">var</span> recordingHints <span style="color: #cda869;">=</span> {
type: <span style="color: #8f9d6a;">'video'</span>
};
<span style="color: #5f5a60; font-style: italic;">// initiating the recorder</span>
<span style="color: #f9ee98;">var</span> recorder <span style="color: #cda869;">=</span> RecordRTC(camera, recordingHints);
<span style="color: #5f5a60; font-style: italic;">// starting recording here</span>
recorder.startRecording();
<span style="color: #5f5a60; font-style: italic;">// auto stop recording after 5 seconds</span>
<span style="color: #f9ee98;">var</span> milliSeconds <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">5</span> <span style="color: #cda869;">*</span> <span style="color: #cf6a4c;">1000</span>;
<span style="color: #dad085;">setTimeout</span>(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// stop recording</span>
recorder.stopRecording(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// get recorded blob</span>
<span style="color: #f9ee98;">var</span> blob <span style="color: #cda869;">=</span> recorder.getBlob();
<span style="color: #5f5a60; font-style: italic;">// open recorded blob in a new window</span>
<span style="color: #9b859d;">window</span>.<span style="color: #dad085;">open</span>( URL.createObjectURL(blob) );
<span style="color: #5f5a60; font-style: italic;">// release camera</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">null</span>;
camera.getTracks().forEach(<span style="color: #f9ee98;">function</span>(track) {
track.<span style="color: #dad085;">stop</span>();
});
<span style="color: #5f5a60; font-style: italic;">// you can preview recorded data on this page as well</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).<span style="color: #cf6a4c;">src</span> <span style="color: #cda869;">=</span> URL.createObjectURL(blob);
});
}, milliSeconds);
});
</<span style="color: #e0c589;">script</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"></<span style="color: #ac885b;">html</span>></span>
</pre>
<br />
<b>Second step</b>, write a PHP file. Lets name it "save.php".<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><?php
<span style="color: #5f5a60; font-style: italic;">// upload directory</span>
<span style="color: #7587a6;">$filePath</span> <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'uploads/'</span> <span style="color: #cda869;">.</span> <span style="color: #7587a6;">$_POST</span>[<span style="color: #8f9d6a;">'video-filename'</span>];
<span style="color: #5f5a60; font-style: italic;">// path to ~/tmp directory</span>
<span style="color: #7587a6;">$tempName</span> <span style="color: #cda869;">=</span> <span style="color: #7587a6;">$_FILES</span>[<span style="color: #8f9d6a;">'video-blob'</span>][<span style="color: #8f9d6a;">'tmp_name'</span>];
<span style="color: #5f5a60; font-style: italic;">// move file from ~/tmp to "uploads" directory</span>
<span style="color: #cda869;">if</span> (<span style="color: #cda869;">!</span><span style="color: #dad085;">move_uploaded_file</span>(<span style="color: #7587a6;">$tempName</span>, <span style="color: #7587a6;">$filePath</span>)) {
<span style="color: #5f5a60; font-style: italic;">// failure report</span>
<span style="color: #dad085;">echo</span> <span style="color: #8f9d6a;">'Problem saving file: '</span><span style="color: #cda869;">.</span><span style="color: #7587a6;">$tempName</span>;
<span style="color: #cda869;"> die</span>();
}
<span style="color: #5f5a60; font-style: italic;">// success report</span>
<span style="color: #dad085;">echo</span> <span style="color: #8f9d6a;">'success'</span>;
?>
</pre>
<br />
<b>Third & last step</b>, upload recorded blob to PHP.<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><span style="color: #f9ee98;">var</span> formData <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">FormData</span>();
<span style="color: #5f5a60; font-style: italic;">// recorded data</span>
formData.append(<span style="color: #8f9d6a;">'video-blob'</span>, recordedBlob);
<span style="color: #5f5a60; font-style: italic;">// file name</span>
formData.append(<span style="color: #8f9d6a;">'video-filename'</span>, recordedBlob.<span style="color: #cf6a4c;">name</span>);
<span style="color: #5f5a60; font-style: italic;">// upload using jQuery</span>
<span style="color: #cda869;">$</span>.ajax({
url: <span style="color: #8f9d6a;">'save.php'</span>, <span style="color: #5f5a60; font-style: italic;">// your server URL</span>
data: formData,
cache: <span style="color: #cf6a4c;">false</span>,
contentType: <span style="color: #cf6a4c;">false</span>,
processData: <span style="color: #cf6a4c;">false</span>,
type: <span style="color: #8f9d6a;">'POST',</span>
<span style="color: #9b703f;">success</span>: <span style="color: #f9ee98;">function</span>(response) {
<span style="color: #cda869;">if</span> (response <span style="color: #cda869;">===</span> <span style="color: #8f9d6a;">'success'</span>) {
<span style="color: #dad085;">alert</span>(<span style="color: #8f9d6a;">'successfully uploaded recorded blob'</span>);
} <span style="color: #cda869;">else</span> {
<span style="color: #dad085;">alert</span>(response);
}
}
});
</pre>
<br />
<h3 style="text-align: left;">
Here is a complete demo, uploading to PHP using jQuery:</h3>
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><span style="color: #ac885b;"><!<span style="color: #494949;"><span style="color: #494949;">DOCTYPE</span> html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">head</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">src</span>=<span style="color: #8f9d6a;">"https://cdn.WebRTC-Experiment.com/RecordRTC.js"</span>></<span style="color: #e0c589;">script</span>>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">src</span>=<span style="color: #8f9d6a;">"https://code.jquery.com/jquery-3.2.1.min.js"</span>></<span style="color: #e0c589;">script</span>>
<<span style="color: #e0c589;">style</span>>
<span style="color: #cda869;">video</span> {
max-width: 100%;
border: 5px solid yellow;
border-radius: 9px;
}
<span style="color: #cda869;">body</span> {
background: black;
}
<span style="color: #cda869;">h1</span> {
color: yellow;
}
</<span style="color: #e0c589;">style</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">head</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">h1</span> <span style="color: #ac885b;">id</span>=<span style="color: #8f9d6a;">"header"</span>></span>RecordRTC Upload to PHP<span style="color: #ac885b;"></<span style="color: #ac885b;">h1</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">video</span> <span style="color: #ac885b;">id</span>=<span style="color: #8f9d6a;">"your-video-id"</span> <span style="color: #ac885b;">controls</span>=<span style="color: #8f9d6a;">""</span> <span style="color: #ac885b;">autoplay</span>=<span style="color: #8f9d6a;">""</span>></<span style="color: #ac885b;">video</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">type</span>=<span style="color: #8f9d6a;">"text/javascript"</span>>
<span style="color: #5f5a60; font-style: italic;">// capture camera and/or microphone</span>
<span style="color: #9b859d;">navigator</span>.mediaDevices.getUserMedia({ video: <span style="color: #cf6a4c;">true</span>, audio: <span style="color: #cf6a4c;">true</span> }).then(<span style="color: #f9ee98;">function</span>(camera) {
<span style="color: #5f5a60; font-style: italic;">// preview camera during recording</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).muted <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">true</span>;
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> camera;
<span style="color: #5f5a60; font-style: italic;">// recording configuration/hints/parameters</span>
<span style="color: #f9ee98;">var</span> recordingHints <span style="color: #cda869;">=</span> {
type: <span style="color: #8f9d6a;">'video'</span>
};
<span style="color: #5f5a60; font-style: italic;">// initiating the recorder</span>
<span style="color: #f9ee98;">var</span> recorder <span style="color: #cda869;">=</span> RecordRTC(camera, recordingHints);
<span style="color: #5f5a60; font-style: italic;">// starting recording here</span>
recorder.startRecording();
<span style="color: #5f5a60; font-style: italic;">// auto stop recording after 5 seconds</span>
<span style="color: #f9ee98;">var</span> milliSeconds <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">5</span> <span style="color: #cda869;">*</span> <span style="color: #cf6a4c;">1000</span>;
<span style="color: #dad085;">setTimeout</span>(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// stop recording</span>
recorder.stopRecording(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// get recorded blob</span>
<span style="color: #f9ee98;">var</span> blob <span style="color: #cda869;">=</span> recorder.getBlob();
<span style="color: #5f5a60; font-style: italic;">// generating a random file name</span>
<span style="color: #f9ee98;">var</span> fileName <span style="color: #cda869;">=</span> getFileName(<span style="color: #8f9d6a;">'webm'</span>);
<span style="color: #5f5a60; font-style: italic;">// we need to upload "File" --- not "Blob"</span>
<span style="color: #f9ee98;">var</span> fileObject <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">File</span>([blob], fileName, {
type: <span style="color: #8f9d6a;">'video/webm'</span>
});
<span style="color: #f9ee98;">var</span> formData <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">FormData</span>();
<span style="color: #5f5a60; font-style: italic;">// recorded data</span>
formData.append(<span style="color: #8f9d6a;">'video-blob'</span>, fileObject);
<span style="color: #5f5a60; font-style: italic;">// file name</span>
formData.append(<span style="color: #8f9d6a;">'video-filename'</span>, fileObject.<span style="color: #cf6a4c;">name</span>);
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'header'</span>).innerHTML <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'Uploading to PHP using jQuery.... file size: ('</span> <span style="color: #cda869;">+</span> bytesToSize(fileObject.<span style="color: #cf6a4c;">size</span>) <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">')'</span>;
<span style="color: #5f5a60; font-style: italic;">// upload using jQuery</span>
<span style="color: #cda869;">$</span>.ajax({
url: <span style="color: #8f9d6a;">'https://webrtcweb.com/RecordRTC/'</span>, <span style="color: #5f5a60; font-style: italic;">// replace with your own server URL</span>
data: formData,
cache: <span style="color: #cf6a4c;">false</span>,
contentType: <span style="color: #cf6a4c;">false</span>,
processData: <span style="color: #cf6a4c;">false</span>,
type: <span style="color: #8f9d6a;">'POST'</span>,
<span style="color: #9b703f;">success</span>: <span style="color: #f9ee98;">function</span>(response) {
<span style="color: #cda869;">if</span> (response <span style="color: #cda869;">===</span> <span style="color: #8f9d6a;">'success'</span>) {
<span style="color: #dad085;">alert</span>(<span style="color: #8f9d6a;">'successfully uploaded recorded blob'</span>);
<span style="color: #5f5a60; font-style: italic;">// file path on server</span>
<span style="color: #f9ee98;">var</span> fileDownloadURL <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'https://webrtcweb.com/RecordRTC/uploads/'</span> <span style="color: #cda869;">+</span> fileObject.<span style="color: #cf6a4c;">name</span>;
<span style="color: #5f5a60; font-style: italic;">// preview the uploaded file URL</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'header'</span>).innerHTML <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'<a href="'</span> <span style="color: #cda869;">+</span> fileDownloadURL <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'" target="_blank">'</span> <span style="color: #cda869;">+</span> fileDownloadURL <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'</a>'</span>;
<span style="color: #5f5a60; font-style: italic;">// preview uploaded file in a VIDEO element</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).<span style="color: #cf6a4c;">src</span> <span style="color: #cda869;">=</span> fileDownloadURL;
<span style="color: #5f5a60; font-style: italic;">// open uploaded file in a new tab</span>
<span style="color: #9b859d;">window</span>.<span style="color: #dad085;">open</span>(fileDownloadURL);
} <span style="color: #cda869;">else</span> {
<span style="color: #dad085;">alert</span>(response); <span style="color: #5f5a60; font-style: italic;">// error/failure</span>
}
}
});
<span style="color: #5f5a60; font-style: italic;">// release camera</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">null</span>;
camera.getTracks().forEach(<span style="color: #f9ee98;">function</span>(track) {
track.<span style="color: #dad085;">stop</span>();
});
});
}, milliSeconds);
});
<span style="color: #5f5a60; font-style: italic;">// this function is used to generate random file name</span>
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">getFileName</span>(fileExtension) {
<span style="color: #f9ee98;">var</span> d <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">Date</span>();
<span style="color: #f9ee98;">var</span> year <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCFullYear</span>();
<span style="color: #f9ee98;">var</span> month <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCMonth</span>();
<span style="color: #f9ee98;">var</span> date <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCDate</span>();
<span style="color: #cda869;">return</span> <span style="color: #8f9d6a;">'RecordRTC-'</span> <span style="color: #cda869;">+</span> year <span style="color: #cda869;">+</span> month <span style="color: #cda869;">+</span> date <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'-'</span> <span style="color: #cda869;">+</span> getRandomString() <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'.'</span> <span style="color: #cda869;">+</span> fileExtension;
}
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">getRandomString</span>() {
<span style="color: #cda869;">if</span> (<span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span> <span style="color: #cda869;">&</span><span style="color: #cda869;">&</span> <span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span>.getRandomValues <span style="color: #cda869;">&</span><span style="color: #cda869;">&</span> <span style="color: #9b859d;">navigator</span>.<span style="color: #cf6a4c;">userAgent</span>.<span style="color: #dad085;">indexOf</span>(<span style="color: #8f9d6a;">'Safari'</span>) <span style="color: #cda869;">===</span> <span style="color: #cda869;">-</span><span style="color: #cf6a4c;">1</span>) {
<span style="color: #f9ee98;">var</span> a <span style="color: #cda869;">=</span> <span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span>.getRandomValues(<span style="color: #cda869;">new</span> <span style="color: #9b703f;">Uint32Array</span>(<span style="color: #cf6a4c;">3</span>)),
token <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">''</span>;
<span style="color: #cda869;">for</span> (<span style="color: #f9ee98;">var</span> i <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">0</span>, l <span style="color: #cda869;">=</span> a.<span style="color: #cf6a4c;">length</span>; i <span style="color: #cda869;"><</span> l; i<span style="color: #cda869;">++</span>) {
token <span style="color: #cda869;">+</span><span style="color: #cda869;">=</span> a[i].<span style="color: #dad085;">toString</span>(<span style="color: #cf6a4c;">36</span>);
}
<span style="color: #cda869;">return</span> token;
} <span style="color: #cda869;">else</span> {
<span style="color: #cda869;">return</span> (<span style="color: #9b859d;">Math</span>.<span style="color: #dad085;">random</span>() <span style="color: #cda869;">*</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">Date</span>().<span style="color: #dad085;">getTime</span>()).<span style="color: #dad085;">toString</span>(<span style="color: #cf6a4c;">36</span>).<span style="color: #dad085;">replace</span>(<span style="color: #e9c062;">/<span style="color: #cf7d34;">\.</span>/g</span>, <span style="color: #8f9d6a;">''</span>);
}
}
</<span style="color: #e0c589;">script</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"></<span style="color: #ac885b;">html</span>></span>
</pre>
<br />
<h3 style="text-align: left;">
Here is a demo uploading to PHP using simple JavaScript (no-jQurery):</h3>
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><span style="color: #ac885b;"><!<span style="color: #494949;"><span style="color: #494949;">DOCTYPE</span> html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">html</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">head</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">src</span>=<span style="color: #8f9d6a;">"https://cdn.WebRTC-Experiment.com/RecordRTC.js"</span>></<span style="color: #e0c589;">script</span>>
<<span style="color: #e0c589;">style</span>>
<span style="color: #cda869;">video</span> {
max-width: 100%;
border: 5px solid yellow;
border-radius: 9px;
}
<span style="color: #cda869;">body</span> {
background: black;
}
<span style="color: #cda869;">h1</span> {
color: yellow;
}
</<span style="color: #e0c589;">style</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">head</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">h1</span> <span style="color: #ac885b;">id</span>=<span style="color: #8f9d6a;">"header"</span>></span>RecordRTC Upload to PHP<span style="color: #ac885b;"></<span style="color: #ac885b;">h1</span>></span>
<span style="color: #ac885b;"><<span style="color: #ac885b;">video</span> <span style="color: #ac885b;">id</span>=<span style="color: #8f9d6a;">"your-video-id"</span> <span style="color: #ac885b;">controls</span>=<span style="color: #8f9d6a;">""</span> <span style="color: #ac885b;">autoplay</span>=<span style="color: #8f9d6a;">""</span>></<span style="color: #ac885b;">video</span>></span>
<<span style="color: #e0c589;">script</span> <span style="color: #e0c589;">type</span>=<span style="color: #8f9d6a;">"text/javascript"</span>>
<span style="color: #5f5a60; font-style: italic;">// capture camera and/or microphone</span>
<span style="color: #9b859d;">navigator</span>.mediaDevices.getUserMedia({ video: <span style="color: #cf6a4c;">true</span>, audio: <span style="color: #cf6a4c;">true</span> }).then(<span style="color: #f9ee98;">function</span>(camera) {
<span style="color: #5f5a60; font-style: italic;">// preview camera during recording</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).muted <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">true</span>;
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> camera;
<span style="color: #5f5a60; font-style: italic;">// recording configuration/hints/parameters</span>
<span style="color: #f9ee98;">var</span> recordingHints <span style="color: #cda869;">=</span> {
type: <span style="color: #8f9d6a;">'video'</span>
};
<span style="color: #5f5a60; font-style: italic;">// initiating the recorder</span>
<span style="color: #f9ee98;">var</span> recorder <span style="color: #cda869;">=</span> RecordRTC(camera, recordingHints);
<span style="color: #5f5a60; font-style: italic;">// starting recording here</span>
recorder.startRecording();
<span style="color: #5f5a60; font-style: italic;">// auto stop recording after 5 seconds</span>
<span style="color: #f9ee98;">var</span> milliSeconds <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">5</span> <span style="color: #cda869;">*</span> <span style="color: #cf6a4c;">1000</span>;
<span style="color: #dad085;">setTimeout</span>(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// stop recording</span>
recorder.stopRecording(<span style="color: #f9ee98;">function</span>() {
<span style="color: #5f5a60; font-style: italic;">// get recorded blob</span>
<span style="color: #f9ee98;">var</span> blob <span style="color: #cda869;">=</span> recorder.getBlob();
<span style="color: #5f5a60; font-style: italic;">// generating a random file name</span>
<span style="color: #f9ee98;">var</span> fileName <span style="color: #cda869;">=</span> getFileName(<span style="color: #8f9d6a;">'webm'</span>);
<span style="color: #5f5a60; font-style: italic;">// we need to upload "File" --- not "Blob"</span>
<span style="color: #f9ee98;">var</span> fileObject <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">File</span>([blob], fileName, {
type: <span style="color: #8f9d6a;">'video/webm'</span>
});
uploadToPHPServer(fileObject, <span style="color: #f9ee98;">function</span>(response, fileDownloadURL) {
<span style="color: #cda869;">if</span>(response <span style="color: #cda869;">!</span><span style="color: #cda869;">==</span> <span style="color: #8f9d6a;">'ended'</span>) {
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'header'</span>).innerHTML <span style="color: #cda869;">=</span> response; <span style="color: #5f5a60; font-style: italic;">// upload progress</span>
<span style="color: #cda869;">return</span>;
}
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'header'</span>).innerHTML <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'<a href="'</span> <span style="color: #cda869;">+</span> fileDownloadURL <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'" target="_blank">'</span> <span style="color: #cda869;">+</span> fileDownloadURL <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'</a>'</span>;
<span style="color: #dad085;">alert</span>(<span style="color: #8f9d6a;">'Successfully uploaded recorded blob.'</span>);
<span style="color: #5f5a60; font-style: italic;">// preview uploaded file</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).<span style="color: #cf6a4c;">src</span> <span style="color: #cda869;">=</span> fileDownloadURL;
<span style="color: #5f5a60; font-style: italic;">// open uploaded file in a new tab</span>
<span style="color: #9b859d;">window</span>.<span style="color: #dad085;">open</span>(fileDownloadURL);
});
<span style="color: #5f5a60; font-style: italic;">// release camera</span>
<span style="color: #9b859d;">document</span>.<span style="color: #dad085;">getElementById</span>(<span style="color: #8f9d6a;">'your-video-id'</span>).srcObject <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">null</span>;
camera.getTracks().forEach(<span style="color: #f9ee98;">function</span>(track) {
track.<span style="color: #dad085;">stop</span>();
});
});
}, milliSeconds);
});
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">uploadToPHPServer</span>(blob, callback) {
<span style="color: #5f5a60; font-style: italic;">// create FormData</span>
<span style="color: #f9ee98;">var</span> formData <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">FormData</span>();
formData.append(<span style="color: #8f9d6a;">'video-filename'</span>, blob.<span style="color: #cf6a4c;">name</span>);
formData.append(<span style="color: #8f9d6a;">'video-blob'</span>, blob);
callback(<span style="color: #8f9d6a;">'Uploading recorded-file to server.'</span>);
makeXMLHttpRequest(<span style="color: #8f9d6a;">'https://webrtcweb.com/RecordRTC/'</span>, formData, <span style="color: #f9ee98;">function</span>(progress) {
<span style="color: #cda869;">if</span> (progress <span style="color: #cda869;">!</span><span style="color: #cda869;">==</span> <span style="color: #8f9d6a;">'upload-ended'</span>) {
callback(progress);
<span style="color: #cda869;">return</span>;
}
<span style="color: #f9ee98;">var</span> initialURL <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">'https://webrtcweb.com/RecordRTC/uploads/'</span> <span style="color: #cda869;">+</span> blob.<span style="color: #cf6a4c;">name</span>;
callback(<span style="color: #8f9d6a;">'ended'</span>, initialURL);
});
}
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">makeXMLHttpRequest</span>(url, data, callback) {
<span style="color: #f9ee98;">var</span> request <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">XMLHttpRequest</span>();
<span style="color: #9b859d;">request</span>.<span style="color: #9b703f;">onreadystatechange</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>() {
<span style="color: #cda869;">if</span> (request.<span style="color: #cf6a4c;">readyState</span> <span style="color: #cda869;">==</span> <span style="color: #cf6a4c;">4</span> <span style="color: #cda869;">&</span><span style="color: #cda869;">&</span> request.<span style="color: #cf6a4c;">status</span> <span style="color: #cda869;">==</span> <span style="color: #cf6a4c;">200</span>) {
<span style="color: #cda869;">if</span> (request.<span style="color: #cf6a4c;">responseText</span> <span style="color: #cda869;">===</span> <span style="color: #8f9d6a;">'success'</span>) {
callback(<span style="color: #8f9d6a;">'upload-ended'</span>);
<span style="color: #cda869;">return</span>;
}
<span style="color: #dad085;">alert</span>(request.<span style="color: #cf6a4c;">responseText</span>);
<span style="color: #cda869;">return</span>;
}
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onloadstart</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>() {
callback(<span style="color: #8f9d6a;">'PHP upload started...'</span>);
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onprogress</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>(event) {
callback(<span style="color: #8f9d6a;">'PHP upload Progress '</span> <span style="color: #cda869;">+</span> <span style="color: #9b859d;">Math</span>.<span style="color: #dad085;">round</span>(<span style="color: #9b859d;">event</span>.loaded / <span style="color: #9b859d;">event</span>.total <span style="color: #cda869;">*</span> <span style="color: #cf6a4c;">100</span>) <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">"%"</span>);
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onload</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>() {
callback(<span style="color: #8f9d6a;">'progress-about-to-end'</span>);
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onload</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>() {
callback(<span style="color: #8f9d6a;">'PHP upload ended. Getting file URL.'</span>);
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onerror</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>(error) {
callback(<span style="color: #8f9d6a;">'PHP upload failed.'</span>);
};
<span style="color: #9b859d;">request.upload</span>.<span style="color: #9b703f;">onabort</span> <span style="color: #cda869;">=</span> <span style="color: #f9ee98;">function</span>(error) {
callback(<span style="color: #8f9d6a;">'PHP upload aborted.'</span>);
};
request.<span style="color: #dad085;">open</span>(<span style="color: #8f9d6a;">'POST'</span>, url);
request.<span style="color: #dad085;">send</span>(data);
}
<span style="color: #5f5a60; font-style: italic;">// this function is used to generate random file name</span>
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">getFileName</span>(fileExtension) {
<span style="color: #f9ee98;">var</span> d <span style="color: #cda869;">=</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">Date</span>();
<span style="color: #f9ee98;">var</span> year <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCFullYear</span>();
<span style="color: #f9ee98;">var</span> month <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCMonth</span>();
<span style="color: #f9ee98;">var</span> date <span style="color: #cda869;">=</span> d.<span style="color: #dad085;">getUTCDate</span>();
<span style="color: #cda869;">return</span> <span style="color: #8f9d6a;">'RecordRTC-'</span> <span style="color: #cda869;">+</span> year <span style="color: #cda869;">+</span> month <span style="color: #cda869;">+</span> date <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'-'</span> <span style="color: #cda869;">+</span> getRandomString() <span style="color: #cda869;">+</span> <span style="color: #8f9d6a;">'.'</span> <span style="color: #cda869;">+</span> fileExtension;
}
<span style="color: #f9ee98;">function</span> <span style="color: #9b703f;">getRandomString</span>() {
<span style="color: #cda869;">if</span> (<span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span> <span style="color: #cda869;">&</span><span style="color: #cda869;">&</span> <span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span>.getRandomValues <span style="color: #cda869;">&</span><span style="color: #cda869;">&</span> <span style="color: #9b859d;">navigator</span>.<span style="color: #cf6a4c;">userAgent</span>.<span style="color: #dad085;">indexOf</span>(<span style="color: #8f9d6a;">'Safari'</span>) <span style="color: #cda869;">===</span> <span style="color: #cda869;">-</span><span style="color: #cf6a4c;">1</span>) {
<span style="color: #f9ee98;">var</span> a <span style="color: #cda869;">=</span> <span style="color: #9b859d;">window</span>.<span style="color: #cf6a4c;">crypto</span>.getRandomValues(<span style="color: #cda869;">new</span> <span style="color: #9b703f;">Uint32Array</span>(<span style="color: #cf6a4c;">3</span>)),
token <span style="color: #cda869;">=</span> <span style="color: #8f9d6a;">''</span>;
<span style="color: #cda869;">for</span> (<span style="color: #f9ee98;">var</span> i <span style="color: #cda869;">=</span> <span style="color: #cf6a4c;">0</span>, l <span style="color: #cda869;">=</span> a.<span style="color: #cf6a4c;">length</span>; i <span style="color: #cda869;"><</span> l; i<span style="color: #cda869;">++</span>) {
token <span style="color: #cda869;">+</span><span style="color: #cda869;">=</span> a[i].<span style="color: #dad085;">toString</span>(<span style="color: #cf6a4c;">36</span>);
}
<span style="color: #cda869;">return</span> token;
} <span style="color: #cda869;">else</span> {
<span style="color: #cda869;">return</span> (<span style="color: #9b859d;">Math</span>.<span style="color: #dad085;">random</span>() <span style="color: #cda869;">*</span> <span style="color: #cda869;">new</span> <span style="color: #9b703f;">Date</span>().<span style="color: #dad085;">getTime</span>()).<span style="color: #dad085;">toString</span>(<span style="color: #cf6a4c;">36</span>).<span style="color: #dad085;">replace</span>(<span style="color: #e9c062;">/<span style="color: #cf7d34;">\.</span>/g</span>, <span style="color: #8f9d6a;">''</span>);
}
}
</<span style="color: #e0c589;">script</span>>
<span style="color: #ac885b;"></<span style="color: #ac885b;">body</span>></span>
<span style="color: #ac885b;"></<span style="color: #ac885b;">html</span>></span>
</pre>
<br />
Want to use up-to-dated PHP file uploading code? Download this file:<br />
<br />
<a href="https://github.com/muaz-khan/RecordRTC/blob/master/RecordRTC-to-PHP/save.php">https://github.com/muaz-khan/RecordRTC/blob/master/RecordRTC-to-PHP/save.php</a><br />
<br />
Having PHP upload issues on your own server? Please check this wiki:<br />
<br />
<a href="https://github.com/muaz-khan/RecordRTC/wiki/PHP-Upload-Issues">https://github.com/muaz-khan/RecordRTC/wiki/PHP-Upload-Issues</a><br />
<br />
Hints for PHP upload issues:<br />
<ul style="text-align: left;">
<li>You need to enable read-write access for "uploads" directory</li>
<li>You need to allow "save.php" file to move uploaded files from ~/tmp directory to your "./uploads" directory.</li>
<li>You need to set "post_max_size" to at least 100 MB</li>
<li>You need to set "upload_max_filesize" to at least 100 MB</li>
<li>You need to set "max_input_time" to at least 10800 (or at least 2-3 minutes)</li>
<li>For detailed information, please <a href="https://github.com/muaz-khan/RecordRTC/wiki/PHP-Upload-Issues" target="_blank">check the wiki page</a></li>
</ul>
<h3 style="text-align: left;">
How PHP saves the recorded blob?</h3>
<br />
<b>First step</b>, PHP reads "video-filename" HTTP_POST parameter. This parameter helps us set uploaded file name.<br />
<br />
<b>Second step</b>, PHP checks for "video-blob" HTTP_POST (i.e. $_FILES) parameter. This parameter allows us get uploaded file data.<br />
<br />
<b>Third step</b>, "video-blob" parameter has a nested "tmp_name" paremter i.e.<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;">$tmp_name = $_FILES['video-blob']['tmp_name'];
</pre>
<br />
"tmp_name" parameter helps us locate the uploaded file on ~/tmp directory.<br />
<br />
Which means that file is already uploaded to ~/tmp directory. We simply need to move file from ~/tmp to our custom location.<br />
<br />
That's why we use "move_upload_file" method.<br />
<br />
<b>Fourth and last step</b>, use "move_upload_file" method to move uploaded file from ~/tmp to "./uploads" directory.<br />
<br />
"move_upload_file" takes two parameters. First parameter is ~/tmp/fileName.webm and second parameter is "./uploads/fileName.webm".<br />
<br />
This method fails in following cases:<br />
<br />
<ul style="text-align: left;">
<li>File was not uploaded to ~/tmp directory; reason: max_upload_filesize or upload_timeout issues.</li>
<li>"./uploads" directory doesn't allows "save.php" file to move file. i.e. you did not set read-write privileges for "./uploads" directory.</li>
</ul>
<br />
Here is how to set read-write privileges for "./uploads" directory (using PHP):<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;"><?php
<span style="color: #5f5a60; font-style: italic;">// Read and write for owner, nothing for everybody else</span>
<span style="color: #dad085;">chmod</span>(<span style="color: #8f9d6a;">"./uploads"</span>, <span style="color: #cf6a4c;">0600</span>);
<span style="color: #5f5a60; font-style: italic;">// Read and write for owner, read for everybody else</span>
<span style="color: #dad085;">chmod</span>(<span style="color: #8f9d6a;">"./uploads"</span>, <span style="color: #cf6a4c;">0644</span>);
<span style="color: #5f5a60; font-style: italic;">// Everything for owner, read and execute for others</span>
<span style="color: #dad085;">chmod</span>(<span style="color: #8f9d6a;">"./uploads"</span>, <span style="color: #cf6a4c;">0755</span>);
<span style="color: #5f5a60; font-style: italic;">// Everything for owner, read and execute for owner's group</span>
<span style="color: #dad085;">chmod</span>(<span style="color: #8f9d6a;">"./uploads"</span>, <span style="color: #cf6a4c;">0750</span>);
?>
</pre>
<br />
However BASH based privileges are preferred/recommended:<br />
<br />
<pre style="background: #181818; border-radius: 3px; color: #f8f8f8; padding: 10px 20px;">[sudo] chmod 755 ./uploads
[sudo] chown -R www-data:www-data ./uploads</pre>
<br />
You can use "apache:apache" if "www-data:www-data" doesn't works above.<br />
<br />
Searching for RecordRTC simple demos? Please check this directory:<br />
<br />
<a href="https://github.com/muaz-khan/RecordRTC/tree/master/simple-demos">https://github.com/muaz-khan/RecordRTC/tree/master/simple-demos</a><br />
<br />
Have issues/bugs/questions, please report here: <a href="https://github.com/muaz-khan/RecordRTC/issues/new">https://github.com/muaz-khan/RecordRTC/issues/new</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-23286585303628771722017-07-31T11:08:00.000+05:002017-07-31T11:08:16.586+05:00Take photo from a webcam using JavaScript<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
Do you want to capture photos from your web-camera and generate images in any supported format e.g. PNG, JPEG, GIF, WebP etc.?<br />
<br />
<span style="font-size: large;">First method, capture photo using Canvas2D API:</span><br />
<br />
<b>Live demo:</b> <a href="https://www.webrtc-experiment.com/takePhoto/">https://www.webrtc-experiment.com/takePhoto/</a><br />
<br />
Below example captures photo from an HTML5 Video element:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> yourVideoElement <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.querySelector<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>video<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// pass your HTML5 Video Element</span>
<span style="color: #268bd2;">var</span> photo <span style="color: #859900;">=</span> takePhoto<span style="color: #93a1a1;">(</span>yourVideoElement<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">window</span>.<span style="color: #a57800;">open</span><span style="color: #93a1a1;">(</span>photo<span style="color: #93a1a1;">)</span>;
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> takePhoto(video)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> canvas <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>canvas<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
canvas.width <span style="color: #859900;">=</span> video.videoWidth <span style="color: #859900;">||</span> video.clientWidth;
canvas.height <span style="color: #859900;">=</span> video.videoHeight <span style="color: #859900;">||</span> video.clientHeight;
<span style="color: #268bd2;">var</span> context <span style="color: #859900;">=</span> canvas.getContext<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>2d<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
context.drawImage<span style="color: #93a1a1;">(</span>video, <span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>, canvas.width, canvas.height<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">return</span> canvas.toDataURL<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>image/png<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span></pre>
<br />
Above code returns PNG. Here is how to get different formats:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> PNG <span style="color: #859900;">=</span> canvas.toDataURL<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>image/png<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> JPEG <span style="color: #859900;">=</span> canvas.toDataURL<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>image/jpeg<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> GIF <span style="color: #859900;">=</span> canvas.toDataURL<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>image/gif<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> WebP <span style="color: #859900;">=</span> canvas.toDataURL<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>image/webp<span style="color: #c60000;">'</span></span>, <span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span>;</pre>
<br />
<br /></div>
<span style="font-size: large;">Second method, capture photo using "<a href="https://github.com/w3c/mediacapture-image" target="_blank">ImageCapture</a>" API:</span><br />
<br />
<b>Live Demo:</b> <a href="https://www.webrtc-experiment.com/ImageCapture/">https://www.webrtc-experiment.com/ImageCapture/</a><br />
<br />
You need to pass MediaStreamTrack (aka Video-Track) object:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> ImageCapture <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> firstVideoTrack <span style="color: #859900;">=</span> yourCameraStream.getVideoTracks<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span><span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span>;
<span style="color: #268bd2;">var</span> imageCapture <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> ImageCapture<span style="color: #93a1a1;">(</span>firstVideoTrack<span style="color: #93a1a1;">)</span>;
imageCapture.takePhoto().then<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">function</span><span style="color: #93a1a1;">(</span>blob<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> photo <span style="color: #859900;">=</span> URL.createObjectURL<span style="color: #93a1a1;">(</span>blob<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">window</span>.<span style="color: #a57800;">open</span><span style="color: #93a1a1;">(</span>photo<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span></pre>
<br />
<b>Note:</b> ImageCapture API requires Chrome version >= 60.</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-71550246317176253112017-04-29T11:07:00.000+05:002017-04-29T11:07:00.619+05:00WebRTC captureStream API<div dir="ltr" style="text-align: left;" trbidi="on">
You can use "captureStream" method to generate a realtime media stream from any HTML5 video or canvas-2d element.<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> realtimeStream <span style="color: #859900;">=</span> canvasElement.captureStream<span style="color: #93a1a1;">(</span><span style="color: #269186;">15</span><span style="color: #93a1a1;">)</span>;</pre>
<h3 style="text-align: left;">
Using captureStream API:</h3>
<ul style="text-align: left;">
<li>You can record <a href="https://www.webrtc-experiment.com/RecordRTC/webgl/" target="_blank">WebGL</a> based 3D games; you can also share in realtime with many users using RTCPeerConnection API</li>
<li>You can record canvas-2D <a href="https://www.webrtc-experiment.com/RecordRTC/Canvas-Recording/Canvas-Animation-Recording.html" target="_blank">animation</a> or <a href="https://www.webrtc-experiment.com/Canvas-Designer/" target="_blank">drawings</a>; you can share in realtime as well using same RTCPeerConnection API</li>
<li>You can share your local video files e.g. WebM, Mp4, FLV, TS-M3U8 (hls-live-streaming) or audio files e.g. Wav, Ogg, Mp3 etc. You can record specific portions of these files as well.</li>
</ul>
<div>
<h3 style="text-align: left;">
How to record WebGL or Canvas2D animations?</h3>
</div>
You can <a href="https://www.youtube.com/playlist?list=PLPRQUXAnRydLsLP_T1JU5MneCu5O86Wj7">use RecordRTC</a> to record canvas-2D or 3D animations. You can try a few demos here:<br />
<ul style="text-align: left;">
<li><a href="https://www.webrtc-experiment.com/RecordRTC/Canvas-Recording/">https://www.webrtc-experiment.com/RecordRTC/Canvas-Recording/</a></li>
</ul>
<b>First step</b>, generate a realtime stream from HTML5 canvas element:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> realtimeStream <span style="color: #859900;">=</span> canvasElement.captureStream<span style="color: #93a1a1;">(</span><span style="color: #269186;">15</span><span style="color: #93a1a1;">)</span>;</pre>
The parameter "15" is named as "frame-rates". We requested 15 fps stream.<br />
<br />
<b>Second step</b>, record the resulting stream using <a href="https://github.com/muaz-khan/RecordRTC">RecordRTC</a> or <a href="https://github.com/streamproc/MediaStreamRecorder">MediaStreamRecorder</a> API:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> realtimeStream <span style="color: #859900;">=</span> canvasElement.captureStream<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> parameters <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span>
mimeType: <span style="color: #269186;"><span style="color: #c60000;">'</span>video/webm<span style="color: #c60000;">'</span></span>
<span style="color: #268bd2;">}</span>;
<span style="color: #268bd2;">var</span> recorder <span style="color: #859900;">=</span> RecordRTC<span style="color: #93a1a1;">(</span>realtimeStream, parameters<span style="color: #93a1a1;">)</span>;
recorder.setRecordingDuration<span style="color: #93a1a1;">(</span><span style="color: #269186;">5</span> <span style="color: #859900;">*</span> <span style="color: #269186;">1000</span><span style="color: #93a1a1;">)</span>.onRecordingStopped<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">function</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> recordedVideo <span style="color: #859900;">=</span> recorder.getBlob<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> file <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> File<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">[</span>recordedVideo<span style="color: #268bd2;">]</span>, <span style="color: #269186;"><span style="color: #c60000;">'</span>filename.webm<span style="color: #c60000;">'</span></span>, <span style="color: #268bd2;">{</span>
type: <span style="color: #269186;"><span style="color: #c60000;">'</span>video/webm<span style="color: #c60000;">'</span></span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> fileURL <span style="color: #859900;">=</span> URL.createObjectURL<span style="color: #93a1a1;">(</span>file<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">window</span>.<span style="color: #a57800;">open</span><span style="color: #93a1a1;">(</span>fileURL<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
recorder.startRecording<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;</pre>
<b>Third and last step</b>, repeatedly draw shapes or animations on canvas 2d surface:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> context <span style="color: #859900;">=</span> canvasElement.getContext<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>2d<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> looper()</span> <span style="color: #268bd2;">{</span>
context.clearRect<span style="color: #93a1a1;">(</span><span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>, canvasElement.width, canvasElement.height<span style="color: #93a1a1;">)</span>;
context.drawImage<span style="color: #93a1a1;">(</span>video, <span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>, canvasElement.width, canvasElement.height<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// draw shapes every 10 milliseconds</span>
<span style="color: #268bd2;">setTimeout</span><span style="color: #93a1a1;">(</span>looper, <span style="color: #269186;">10</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;</pre>
<h3 style="text-align: left;">
How to broadcast/share WebGL or Canvas2D animations with other users?</h3>
You can "[RTCPeerConnection] addStream" method to share (broadcast) canvas-2D or 3D animations with remote users. You can try this demo:<br />
<ul style="text-align: left;">
<li><a href="https://rtcmulticonnection.herokuapp.com/demos/Pre-recorded-Media-Streaming.html">https://rtcmulticonnection.herokuapp.com/demos/Pre-recorded-Media-Streaming.html</a></li>
</ul>
<b>First step</b>, generate a realtime stream from HTML5 canvas element:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: consolas, "lucida console", "dejavu sans mono", monaco, "courier new", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> realtimeStream <span style="color: #859900;">=</span> canvasElement.captureStream<span style="color: #93a1a1;">(</span><span style="color: #269186;">15</span><span style="color: #93a1a1;">)</span>;</pre>
<b>Second and last step</b>, share with remote users using RTCPeerConnection API:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> peer <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> webkitRTCPeerConnection<span style="color: #93a1a1;">(</span>parameters<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// check this line</span>
peer.addStream<span style="color: #93a1a1;">(</span>canvasStream<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// now either create offer or answer descriptions</span>
peer.createOffer<span style="color: #93a1a1;">(</span>successCallback, failureCallback, parameters<span style="color: #93a1a1;">)</span>;</pre>
<h3 style="text-align: left;">
How to share or record microphone audio along with Canvas2D animations?</h3>
<div>
It is simple: use "addTrack" method to merge tracks.</div>
<div>
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> microphoneStream <span style="color: #859900;">=</span> captureUsingNavigatorDotGetUserMediaAPI<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">{</span>
audio: <span style="color: #b58900;">true</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// merge audio tracks into canvas stream</span>
microphoneStream.getAudioTracks<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>.forEach<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">function</span><span style="color: #93a1a1;">(</span>track<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// check this line</span>
canvasStream.addTrack<span style="color: #93a1a1;">(</span>track<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// now either record "canvasStream" or share using RTCPeerConnection API</span>
peer.addStream<span style="color: #93a1a1;">(</span>canvasStream<span style="color: #93a1a1;">)</span>; <span style="color: #93a1a1;">// microphone+canvas2D</span></pre>
</div>
<div>
<h3 style="text-align: left;">
Can I capture stream from an <audio> element?</h3>
</div>
Simply yes. You can capture audio either from <audio> tag or otherwise <video> element.<br />
<br />
Remember: <video> tag also accepts audio-only stream. Your stream do NOT need to have video tracks. Here is how to capture audio stream from a <video> element:<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;">videoElement.src <span style="color: #859900;">=</span> URL.createObjectURL<span style="color: #93a1a1;">(</span>microphoneStream<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// capture from <video> tag</span>
<span style="color: #268bd2;">var</span> capturedStream <span style="color: #859900;">=</span> videoElement.captureStream<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// initiate a standalone instance</span>
<span style="color: #268bd2;">var</span> audioOnlyStream <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> webkitMediaStream<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// merge only audio tracks</span>
capturedStream.getAudioTracks<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>.forEach<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">function</span><span style="color: #93a1a1;">(</span>track<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
audioOnlyStream.addTrack<span style="color: #93a1a1;">(</span>track<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// either record "audioOnlyStream" or share using RTCPeerConnection API</span>
peer.addStream<span style="color: #93a1a1;">(</span>audioOnlyStream<span style="color: #93a1a1;">)</span>;</pre>
<h3 style="text-align: left;">
Unable to use "captureStream" API?</h3>
<div style="text-align: left;">
You may need to enable following chrome-flag; then relaunch the chrome browser:</div>
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #269186;">chrome:</span><span style="color: #d30102;">/</span><span style="color: #d30102;"></span><span style="color: #d30102;">/</span>flags<span style="color: #859900;">/</span><span style="color: #93a1a1;">#enable-experimental-web-platform-features</span></pre>
<h3 style="text-align: left;">
References:</h3>
<ul style="text-align: left;">
<li><a href="https://w3c.github.io/mediacapture-fromelement/" target="_blank">Media Capture from DOM Elements</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream" target="_blank">MDN: HTMLMediaElement.captureStream</a></li>
</ul>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-48035855740957549632015-01-03T10:41:00.001+05:002017-04-28T22:28:28.211+05:00Disable ICE Trickling<div dir="ltr" style="text-align: left;" trbidi="on">
ICE trickling is a process where candidate-pairs are shared as soon as gathered by the ICEAgent.<br />
<br />
Its true that, there is NO JavaScript API "currently" available in RTCWeb drafts to disable ICE-trickling process however, there is a trick that can be used to merge all candidate pairs in the session-description, and then you merely need to share that "single" SDP only.<br />
<br />
The trick is simple: <span style="background: yellow; border: 1px dotted red; padding: 2px 3px;">Wait until "end-of-candidate" signal is fired</span>.<br />
<br />
Usually "<code>onicecandidate</code>" event returns "<code>NULL</code>" entry for "<code>event.candidate</code>" object.<br />
<br />
In "old-good" days, we were watching for "<code>oniceconnectionstatechange</code>" event, and checking for "<code>peer.iceGatheringState === 'complete'</code>" to return the SDP.<br />
<br />
BTW, you can still listen for both "<strong>end-of-candidate</strong>" NULL value, as well as "<code>peer.iceGatheringState === 'complete'</code>".<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #859900;">peer</span>.oniceconnectionstatechange <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>(event)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>peer.iceGatheringState <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>complete<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
send_sdp_to_remote_peer<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>;
<span style="color: #a57800;"><span style="color: #859900;">peer</span>.onicecandidate <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>(event)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">event</span>.candidate <span style="color: #859900;">===</span> <span style="color: #b58900;">null</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">return</span> send_sdp_to_remote_peer<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>;
<span style="color: #268bd2;">var</span> isSdpSent <span style="color: #859900;">=</span> <span style="color: #b58900;">false</span>;
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> send_sdp_to_remote_peer()</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>isSdpSent<span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
isSdpSent <span style="color: #859900;">=</span> <span style="color: #b58900;">true</span>;
<span style="color: #268bd2;">var</span> sdp <span style="color: #859900;">=</span> peer.localDescription;
socket.emit<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>remote-sdp<span style="color: #c60000;">'</span></span>, sdp<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span></pre>
<br />
<br /></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-63490233360090667002014-10-15T08:17:00.001+05:002018-10-16T08:01:21.019+05:00WebRTC for ASP.NET developers<div dir="ltr" style="text-align: left;" trbidi="on">
This post links some demos and tutorials for ASP.NET webforms and ASP.NET MVC developers to quick start and implement WebRTC in their applications.<br />
<br />
First of all, please don't forget to check this post: <a href="http://www.muazkhan.com/2013/12/i-want-to-learn-webrtc.html">I want to learn WebRTC!</a><br />
<br />
Here is a simple one-to-one ASP.NET MVC based demo & source code:<br />
<ul style="text-align: left;">
<li><a href="https://github.com/muaz-khan/WebRTC-ASPNET-MVC">https://github.com/muaz-khan/WebRTC-ASPNET-MVC</a></li>
</ul>
Here is ASP.NET webforms based demo:<br />
<ul style="text-align: left;">
<li><a href="https://github.com/muaz-khan/RTCMultiConnection-SignalR">https://github.com/muaz-khan/RTCMultiConnection-SignalR</a></li>
</ul>
<br />
This demo uses MS-SQL to store SDP and ICE messages and to sync the data among room participants. This demo is having following functionalities:<br />
<ol style="text-align: left;">
<li>Private/Public rooms creations</li>
<li>Password protected rooms</li>
<li>MS-SQL for signaling and presence detection</li>
<li>One-to-One connections</li>
<li>List of a public rooms, stats of each room, number of users in each room etc.</li>
</ol>
<br />
There is another XHR-based signaling demo and source code that can fit in any WebRTC application and demo:<br />
<ul style="text-align: left;">
<li><a href="https://github.com/muaz-khan/XHR-Signaling">https://github.com/muaz-khan/XHR-Signaling</a></li>
</ul>
<br />
This demo is having following features:<br />
<ol style="text-align: left;">
<li>It can be used in any <a href="https://github.com/muaz-khan/WebRTC-Experiment">WebRTC Experiment</a></li>
<li>It uses MS-SQL for signaling</li>
<li>It supports re-usability of the code</li>
<li>It can be used with <a href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/RTCMultiConnection">RTCMultiConnection.js</a> and <a href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/DataChannel">DataChannel.js</a></li>
</ol>
<div>
Here is how it can be used in <a href="https://github.com/muaz-khan/RTCMultiConnection/tree/master/demos">RTCMultiConnection demos</a>:</div>
<div>
<ul style="text-align: left;">
<li><a href="http://www.rtcmulticonnection.org/docs/openSignalingChannel/#xhr-signaling">http://www.rtcmulticonnection.org/docs/openSignalingChannel/#xhr-signaling</a></li>
</ul>
</div>
<div>
There is another implementation and source code that uses WebSync:</div>
<div>
<ul style="text-align: left;">
<li><a href="https://github.com/muaz-khan/WebSync-Signaling">https://github.com/muaz-khan/WebSync-Signaling</a></li>
</ul>
</div>
<blockquote class="tr_bq">
WebSync is an implementation of the Bayeux specification, commonly known as "comet", for the .NET framework and IIS. <a href="https://www.frozenmountain.com/products/websync/overview">Ref</a></blockquote>
<div>
Try WebSync demos in action:</div>
<div>
<ul style="text-align: left;">
<li><a href="http://websync.somee.com/">http://websync.somee.com/</a></li>
</ul>
</div>
<div>
For those who wanna use SignalR instead:</div>
<div>
<ul style="text-align: left;">
<li><a href="http://www.rtcmulticonnection.org/docs/openSignalingChannel/#signalr-signaling">http://www.rtcmulticonnection.org/docs/openSignalingChannel/#signalr-signaling</a></li>
</ul>
</div>
<div>
P.S. This post will be updated for future ASP.NET based WebRTC demos e.g. SignalR demos.</div>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-62101895259387466122014-05-01T19:07:00.000+05:002017-04-28T22:27:59.673+05:00WebRTC Tips & Tricks<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: large;"><i>This blog post is added for WebRTC newbies and beginners who wanna learn key-ideas; get code snippets and enjoy WebRTC life!</i></span><br />
<br />
<h2>
1. How to mute/unmute media streams?</h2>
Remember, mute/unmute isn't implemented as a default/native features in either media capturing draft i.e. getUserMedia API also in WebRTC draft i.e. RTCPeerConnection API.<br />
<br />
Also, there is no "<code style="font-family: monospace; font-size: 1.2em;">onmuted</code>" and "<code style="font-family: monospace; font-size: 1.2em;">onunmuted</code>" event defined or fired in the WebRTC native implementations.<br />
<br />
Usually, as per chromium team's suggestions, media-tracks are enabled/disabled to mute/unmute the streams.<br />
<br />
<blockquote class="tr_bq">
<b>Remember</b>, "<code style="font-size: 1.2em;">MediaStreamTrack.enabled=false</code>" NEVER sends <b>silence</b> audio or blank/black video; <i><span style="color: #073763;"><b>it doesn't stop packet transmission</b></span></i>. Although when you set "<code style="font-size: 1.2em;">MediaStreamTracks.enabled=false</code>", <b><i><span style="color: #073763;">packets are devoid of meaningful data</span></i></b>. A solution for this approach is to hold/unhold tracks from SDP and renegotiate the connections. See next section for more information.</blockquote>
<br />
<code style="font-family: monospace; font-size: 1.2em;">MediaStream</code> object is just a synchronous container. You shouldn't consider it an object interacting with the media source (audio/video input/output devices). That's why it was suggested to move "<code style="font-family: monospace; font-size: 1.2em;">stop</code>" method from "<code style="font-family: monospace; font-size: 1.2em;">MediaStream</code>" level to "<code style="font-family: monospace; font-size: 1.2em;">MediaStreamTracks</code>" level.<br />
<br />
MediaStreamTracks are written to define input/output devices' kind. A single <code style="font-family: monospace; font-size: 1.2em;">MediaStreamTrack</code> object can contain multiple media devices.<br />
<br />
MediaStreamTracks has "<code style="font-family: monospace; font-size: 1.2em;">enabled</code>" property as well as "<code style="font-family: monospace; font-size: 1.2em;">stop</code>" method. You can use "<code style="font-family: monospace; font-size: 1.2em;">stop</code>" method to leave relevant media sources — whether it is audio source or video one.<br />
<br />
Mute/UnMute is usually happened by setting Boolean values for the "<code style="font-family: monospace; font-size: 1.2em;">enabled</code>" property per each <code style="font-family: monospace; font-size: 1.2em;">MediaStreamTrack</code>.<br />
<br />
When a MediaStreamTrack is "<code style="font-family: monospace; font-size: 1.2em;">enabled=true</code>" it is unmuted; when a MediaStreamTrack is "<code style="font-family: monospace; font-size: 1.2em;">enabled=false</code>" it is muted.<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> audioTracks <span style="color: #859900;">=</span> localMediaStream.getAudioTracks<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> videoTracks <span style="color: #859900;">=</span> localMediaStream.getVideoTracks<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// if MediaStream has reference to microphone</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>audioTracks<span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
audioTracks<span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span>.enabled <span style="color: #859900;">=</span> <span style="color: #b58900;">false</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// if MediaStream has reference to webcam</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>videoTracks<span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
videoTracks<span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span>.enabled <span style="color: #859900;">=</span> <span style="color: #b58900;">false</span>;
<span style="color: #268bd2;">}</span></pre>
<br />
Keep in mind that you're disabling a media track locally; it will not fire any event on target users side. If you disabled video track; then it will cause "blank-video" on target-users side.<br />
<br />
You can manually fire events like "<code style="font-family: monospace; font-size: 1.2em;">onmuted</code>" or "<code style="font-family: monospace; font-size: 1.2em;">onmediatrackdisabled</code>" by using socket that was used for signaling. You can send/emit messages like:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;">yourSignalingSocket.<span style="color: #268bd2;">send</span><span style="color: #93a1a1;">(</span><span style="color: #268bd2;">{</span>
isMediaStreamTrackDisabled: <span style="color: #b58900;">true</span>,
mediaStreamLabel: stream.label
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;</pre>
<br />
Target users' "<code style="font-family: monospace; font-size: 1.2em;">onmessage</code>" handler can watch for "<code style="font-family: monospace; font-size: 1.2em;">isMediaStreamTrackDisabled</code>" Boolean and fire "<code style="font-family: monospace; font-size: 1.2em;">onmediatrackdisabled</code>" accordingly:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #859900;">yourSignalingSocket</span>.onmessage <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>event)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> data <span style="color: #859900;">=</span> <span style="color: #859900;">event</span>.data;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>data.isMediaStreamTrackDisabled <span style="color: #859900;">==</span> <span style="color: #b58900;">true</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
emitEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>mediatrackdisabled<span style="color: #c60000;">'</span></span>, <span style="color: #b58900;">true</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> emitEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>mediatrackdisabled<span style="color: #c60000;">'</span></span>, <span style="color: #b58900;">false</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>;</pre>
<br />
Last point; disabling "remote" media stream tracks are useless unless remote user disables his "local" media stream tracks. So, <i><b>use signaling-socket, exchange messages between users to inform them enable/disable media tracks and fire relevant events that can be used to display video-posters or overlappers</b>!</i><br />
<i><br />
</i> People usually asks about "<code style="font-size: 1.2em;">video.pause()</code>" and "<code style="font-family: monospace; font-size: 1.2em;">video.muted=true</code>". Remember, you're simply setting playback status of the local video element; it will NEVER synchronize changes to other peers until you listen for "<code style="font-size: 1.2em;">video.onplay</code>" and "<code style="font-size: 1.2em;">video.onpause</code>" handlers.<br />
<br />
You can also listen for "<code style="font-size: 1.2em;">video.onvolumechange</code>" to synchronize volume among all users.<br />
<br />
Remember, you can use WebRTC data channels or websocket or any other signaling mean to synchronize such statuses.<br />
<br />
<h2>
2. How to hold/unhold media calls?</h2>
Currently, WebRTC signaling mechanism is based on offer/answer model which in turns uses session-description protocol (SDP) to exchange metadata and key-session-requirements among peers.<br />
<br />
SDP is formatted in a way that MediaStreamTracks are given unique media-line; and each media line can contain references to multiple similar media tracks.<br />
<br />
Each media-line has "<code style="font-family: monospace; font-size: 1.2em;">a=sendrecv</code>" attribute; which is used to exchange incoming/outgoing media directions. You can easily replace "<code style="font-family: monospace; font-size: 1.2em;">sendrecv</code>" with "<code style="font-family: monospace; font-size: 1.2em;">inactive</code>" to make that media track on hold.<br />
<br />
Don't forget that a single media-line can contain multiple media stream tracks; so if you're planning to hold all audio tracks; then search for audio media-line in the SDP; and replace "<code style="font-family: monospace; font-size: 1.2em;">a=sendrecv</code>" with "<code style="font-family: monospace; font-size: 1.2em;">a=inactive</code>".<br />
<br />
Also, please keep in mind that "<code style="font-family: monospace; font-size: 1.2em;">sendrecv</code>" isn't the only value given to "<code style="font-family: monospace; font-size: 1.2em;">a=</code>" attribute. Possible values are:<br />
<ol>
<li><code style="font-family: monospace; font-size: 1.2em;">sendrecv</code> ——— two-way media flow</li>
<li><code style="font-family: monospace; font-size: 1.2em;">sendonly</code> ——— one-way outgoing media flow</li>
<li><code style="font-family: monospace; font-size: 1.2em;">recvonly</code> ——— one-way incoming media flow</li>
<li><code style="font-family: monospace; font-size: 1.2em;">inactive</code> ——— call on hold; i.e. no media flow</li>
</ol>
<br />
So, you need to replace first three values with "<code style="font-family: monospace; font-size: 1.2em;">inactive</code>" to make list of identical media tracks on hold; then you can replace "<code style="font-family: monospace; font-size: 1.2em;">inactive</code>" with previous value to leave the "hold" status.<br />
<br />
After altering local-session-descriptions (<code style="font-family: monospace; font-size: 1.2em;">peer.localDescription</code>); you MUST renegotiate peer connections to make sure new changes are applied on both peers side.<br />
<br />
Renegotiation is a process to recreate offer/answer descriptions and set remote descriptions again.<br />
<br />
Remember, you're not creating new peer connections; you're using existing peer objects and invoking <code style="font-family: monospace; font-size: 1.2em;">createOffer</code>/<code style="font-family: monospace; font-size: 1.2em;">createAnswer</code> as well as <code style="font-family: monospace; font-size: 1.2em;">setRemoteDescription</code> again.<br />
<br />
Renegotiation works only on chromium based browsers in the moment; so hold/unhold feature will work only on chrome/opera.<br />
<br />
You can learn more about renegotiation here: <a href="https://www.webrtc-experiment.com/docs/how-to-switch-streams.html">https://www.webrtc-experiment.com/docs/how-to-switch-streams.html</a><br />
<br />
<h2>
3. How to check if a peer connection is established?</h2>
Simply set an event listener for "<code style="font-family: monospace; font-size: 1.2em;">oniceconnectionstatechange</code>" and check for "<code style="font-family: monospace; font-size: 1.2em;">peer.iceConnectionState=='completed'</code>".<br />
<br />
ICE connection state <a href="http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtciceconnectionstate-enum">flows</a> like this:<br />
<br />
<pre style="border-left-color: red; border-left-style: solid; border-left-width: 2px; margin-left: 2em; overflow: auto; padding-left: 1em;">(success) new => checking => connected => completed
(failure) new => checking => connected => disconnected => new => closed</pre>
<br />
<img src="https://dev.w3.org/2011/webrtc/editor/images/icestates.svg" style="max-width: 100%;" /><br />
<br />
Sometimes ICE connection gets dropped out of network loss or unexpected situations. In such cases, "ice-connection-state" is always changed to "<code style="font-family: monospace; font-size: 1.2em;">disconnected</code>".<br />
<br />
There is another dangerous point is that if you're <a href="https://www.webrtc-experiment.com/docs/how-to-switch-streams.html">renegotiating peers</a>; then ice-connection-state is always changed to "<code style="font-size: 1.2em;">disconnected</code>" then it restarts: "<code style="font-size: 1.2em;">new => checking => connected => completed => <b><u><span style="color: red;">disconnected</span></u></b> => new => checking => connected => completed</code>"..<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> peer <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> RTCPeerConnection<span style="color: #93a1a1;">(</span>iceSerersArray, optionalArgsArray<span style="color: #93a1a1;">)</span>;
<span style="color: #a57800;"><span style="color: #859900;">peer</span>.oniceconnectionstatechange <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>()</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span>peer.iceConnectionState <span style="color: #859900;">==</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>completed<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> message <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>WebRTC RTP ports are connected to UDP. <span style="color: #c60000;">'</span></span>;
message <span style="color: #859900;">+</span><span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>Wait a few seconds for remote stream to be started flowing.<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">alert</span><span style="color: #93a1a1;">(</span>message<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>;</pre>
<br />
<h2>
4. How to check if a peer connection is closed or dropped?</h2>
Again, watch for "<code style="font-family: monospace; font-size: 1.2em;">oniceconnectionstatechange</code>" event handler and check for "<code style="font-family: monospace; font-size: 1.2em;">peer.iceConnectionState=='disconnected'</code>".<br />
<br />
ICE Agent changes relevant candidates' state to "<code style="font-family: monospace; font-size: 1.2em;">disconnected</code>" in following cases:<br />
<ol>
<li>If peer connection is closed.</li>
<li>If you're renegotiating media.....in this case, previous peer connection is closed and re-established again.</li>
</ol>
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> peer <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> RTCPeerConnection<span style="color: #93a1a1;">(</span>iceSerersArray, optionalArgsArray<span style="color: #93a1a1;">)</span>;
<span style="color: #a57800;"><span style="color: #859900;">peer</span>.oniceconnectionstatechange <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>()</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span>peer.iceConnectionState <span style="color: #859900;">==</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>disconnected<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> message <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>WebRTC RTP ports are closed. <span style="color: #c60000;">'</span></span>;
message <span style="color: #859900;">+</span><span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>UDP connection is dropped.<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">alert</span><span style="color: #93a1a1;">(</span>message<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>;</pre>
<br />
<h2>
5. How to check if browser has microphone or webcam?</h2>
There is a JavaScript library named as "<a href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/DetectRTC">DetectRTC.js</a>"; which uses "<code style="font-family: monospace; font-size: 1.2em;">getSources</code>/<code style="font-family: monospace; font-size: 1.2em;">getMediaDevices</code>" API to fetch list of all audio/video input devices.<br />
<br />
If there is no audio input device; then it says that:<br />
<br />
<blockquote class="tr_bq" style="background: #f1f1f1; border: 1px dotted gray; margin-top: 1em; margin-top: 5px; margin: 1em; padding: 1em;">
Your browser has NO microphone attached or you clicked <b><u>deny</u></b> button sometime and browser is still denying the webpage to access relevant media.</blockquote>
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;">CheckDeviceSupport<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>chrome)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>chrome.hasMicrophone<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>chrome.hasWebcam<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">// above function is defined here</span>
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> CheckDeviceSupport(callback)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> Chrome <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>;
<span style="color: #93a1a1;">// This method is useful only for Chrome!</span>
<span style="color: #93a1a1;">// "navigator.getMediaDevices" will be next!</span>
<span style="color: #93a1a1;">// "MediaStreamTrack.getSources" will be removed.</span>
<span style="color: #93a1a1;">// 1st step: verify "MediaStreamTrack" support.</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.MediaStreamTrack<span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
<span style="color: #93a1a1;">// 2nd step: verify "getSources" support which is planned to be removed soon!</span>
<span style="color: #93a1a1;">// "getSources" will be replaced with "getMediaDevices"</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>MediaStreamTrack.getSources<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
MediaStreamTrack.getSources <span style="color: #859900;">=</span> MediaStreamTrack.getMediaDevices;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// if still no "getSources"; it MUST be firefox!</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>MediaStreamTrack.getSources<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// assuming that it is older chrome or chromium implementation</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">!</span><span style="color: #859900;">navigator</span>.webkitGetUserMedia<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
Chrome.hasMicrophone <span style="color: #859900;">=</span> <span style="color: #b58900;">true</span>;
Chrome.hasWebcam <span style="color: #859900;">=</span> <span style="color: #b58900;">true</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #859900;">return</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// loop over all audio/video input/output devices</span>
MediaStreamTrack.getSources<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>sources)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> result <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>;
<span style="color: #859900;">for</span> <span style="color: #93a1a1;">(</span><span style="color: #268bd2;">var</span> i <span style="color: #859900;">=</span> <span style="color: #269186;">0</span>; i <span style="color: #859900;">&</span>lt; sources.length; i<span style="color: #859900;">++</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
result<span style="color: #268bd2;">[</span>sources<span style="color: #268bd2;">[</span>i<span style="color: #268bd2;">]</span>.kind<span style="color: #268bd2;">]</span> <span style="color: #859900;">=</span> <span style="color: #b58900;">true</span>;
<span style="color: #268bd2;">}</span>
Chrome.hasMicrophone <span style="color: #859900;">=</span> result.audio;
Chrome.hasWebcam <span style="color: #859900;">=</span> result.video;
callback<span style="color: #93a1a1;">(</span>Chrome<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span></pre>
<br />
<br />
<h2>
6. How to fix echo/noise issues?</h2>
According to <a href="http://www.slideshare.net/MuazKhan/echo-in-webrtc-why">this page</a>:<br />
<blockquote class="tr_bq" style="background: #f1f1f1; border: 1px dotted gray; margin-top: 1em; margin-top: 5px; margin: 1em; padding: 1em;">
Echo is a distortion of voice that occurs either when you place input/output audio devices closed together; or audio output level is too high or CPU usage exceeded by other applications or by the same application.</blockquote>
<br />
Firefox 29 and upper builds has a nice echo cancellation. Echo is also improved in chrome 34 and upper builds.<br />
<br />
Make sure that your speaker's kHz values matches with your application's kHz values. Mismatch will lead to echo.<br />
<br />
If you're using Mac OSX; then you can easily recover echo issues by enabling "ambient noise reduction". You can search the Google engine for how to enable for build-in audio devices on Mac.<br />
<br />
It is possible to watch audio RTP-packets' audio level using <code style="font-size: 1.2em;">getStats</code> API; however there is no API other than Media Processing API that allows setting volume from JavaScript applications. Media Processing draft isn't standardized yet and AFAIK, none of the browser vendors implemented volume-specific API. Though, Gecko team implemented <code style="font-size: 1.2em;">captureStreamUntilEnded</code> API in their Firefox product from the same draft.<br />
<br />
Make sure that you're not using WebAudio API along with <code style="font-size: 1.2em;">getUserMedia</code> or <code style="font-size: 1.2em;">RTCPeerConnection</code> API; because sometimes you accidentally or intentionally connect input node with output node which obviously causes huge echo.<br />
<br />
Developers were suggesting headphones/microphones to overcome echo issues however it is appeared that such solutions doesn't matters. One must check all relevant conditions to make sure there is nothing causing noise.<br />
<br />
<h2>
7. How to check estimated bandwidth?</h2>
Sometimes it is known as "available bandwidth" or "bandwidth consumed". You can setup a listener over <code style="font-size: 1.2em;">getStats</code> API to check <code style="font-size: 1.2em;">bytesSent</code> per second. Then you can easily find-out bandwidth that your application is currently consuming.<br />
<br />
Remember, you're checking bandwidth in one-to-one scenario; you need to follow some deeper-level tricks to find estimated bandwidth for multi-peers scenarios.<br />
<br />
Following example assumes that you opened audio-only connection between two users; and you're checking bandwidth consumed by outgoing RTP ports.<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> getStats(peer)</span> <span style="color: #268bd2;">{</span>
_getStats<span style="color: #93a1a1;">(</span>peer, <span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>results)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">for</span> <span style="color: #93a1a1;">(</span><span style="color: #268bd2;">var</span> i <span style="color: #859900;">=</span> <span style="color: #269186;">0</span>; i <span style="color: #859900;">&</span>lt; results.length; <span style="color: #859900;">++</span>i<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> res <span style="color: #859900;">=</span> results<span style="color: #268bd2;">[</span>i<span style="color: #268bd2;">]</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>res.googCodecName <span style="color: #859900;">==</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>opus<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.prevBytesSent<span style="color: #93a1a1;">)</span>
<span style="color: #859900;">window</span>.prevBytesSent <span style="color: #859900;">=</span> res.bytesSent;
<span style="color: #268bd2;">var</span> bytes <span style="color: #859900;">=</span> res.bytesSent <span style="color: #859900;">-</span> <span style="color: #859900;">window</span>.prevBytesSent;
<span style="color: #859900;">window</span>.prevBytesSent <span style="color: #859900;">=</span> res.bytesSent;
<span style="color: #268bd2;">var</span> kilobytes <span style="color: #859900;">=</span> bytes / <span style="color: #269186;">1024</span>;
console<span style="color: #268bd2;">.log</span><span style="color: #93a1a1;">(</span>kilobytes.toFixed<span style="color: #93a1a1;">(</span><span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">+</span> <span style="color: #269186;"><span style="color: #c60000;">'</span> kbits/s<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">setTimeout</span><span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>)</span> <span style="color: #268bd2;">{</span>
getStats<span style="color: #93a1a1;">(</span>peer<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>, <span style="color: #269186;">1000</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// a wrapper around getStats which hides the differences (where possible)</span>
<span style="color: #93a1a1;">// following code-snippet is taken from somewhere on the github</span>
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> _getStats(peer, cb)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">!</span><span style="color: #859900;">navigator</span>.mozGetUserMedia<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
peer.getStats<span style="color: #93a1a1;">(</span>
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>res)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> items <span style="color: #859900;">=</span> <span style="color: #268bd2;">[</span><span style="color: #268bd2;">]</span>;
res.forEach<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>result)</span> <span style="color: #268bd2;">{</span>
items.<span style="color: #268bd2;">push</span><span style="color: #93a1a1;">(</span>result<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
cb<span style="color: #93a1a1;">(</span>items<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>,
cb
<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
peer.getStats<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>res)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> items <span style="color: #859900;">=</span> <span style="color: #268bd2;">[</span><span style="color: #268bd2;">]</span>;
res.result<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>.forEach<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>result)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> item <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>;
result.names<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>.forEach<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>name)</span> <span style="color: #268bd2;">{</span>
item<span style="color: #268bd2;">[</span>name<span style="color: #268bd2;">]</span> <span style="color: #859900;">=</span> result.stat<span style="color: #93a1a1;">(</span>name<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
item.id <span style="color: #859900;">=</span> result.id;
item.type <span style="color: #859900;">=</span> result.type;
item.timestamp <span style="color: #859900;">=</span> result.timestamp;
items.<span style="color: #268bd2;">push</span><span style="color: #93a1a1;">(</span>item<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
cb<span style="color: #93a1a1;">(</span>items<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>;</pre>
<br />
You can use it like this:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #859900;">peer</span>.onaddstream <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>(event)</span> <span style="color: #268bd2;">{</span>
getStats<span style="color: #93a1a1;">(</span>peer<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>;</pre>
<h2>
8. How to listen Audio/Video elements native events?</h2>
You can easily override "<code style="font-size: 1.2em;">onpause</code>", "<code style="font-size: 1.2em;">onplay</code>", and "<code style="font-size: 1.2em;">onvolumechange</code>" events.<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #859900;">htmlVideoElement</span>.onplay <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// this event is fired each time when you playback the video</span>
<span style="color: #93a1a1;">// via play() method or muted=false or paused=false</span>
<span style="color: #268bd2;">}</span>;
<span style="color: #a57800;"><span style="color: #859900;">htmlVideoElement</span>.onpause <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// this event is fired each time when you pause/stop playback</span>
<span style="color: #93a1a1;">// via pause() or muted=true or paused=true or stop()</span>
<span style="color: #268bd2;">}</span>;
<span style="color: #a57800;"><span style="color: #859900;">htmlVideoElement</span>.onvolumechange <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// htmlVideoElement.volume</span>
<span style="color: #268bd2;">}</span>;</pre>
<br />
<h2>
9. How to get list of all audio/video input devices?</h2>
Note: Chromium team is <a href="https://code.google.com/p/chromium/issues/detail?id=338511">implementing</a> "<code style="font-size: 1.2em;">navigator.getMediaDevices</code>" interface which will allow you prefetch both input and output devices.<br />
<br />
Following code snippet uses "<code style="font-size: 1.2em;">MediaStreamTrack.getSources</code>" to fetch-out all input audio/video devices.<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> getInputDevices(callback)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// This method is useful only for Chrome!</span>
<span style="color: #268bd2;">var</span> devicesFetched <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>;
<span style="color: #93a1a1;">// 1st step: verify "MediaStreamTrack" support.</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.MediaStreamTrack <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; <span style="color: #859900;">!</span><span style="color: #859900;">navigator</span>.getMediaDevices<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">return</span> callback<span style="color: #93a1a1;">(</span>devicesFetched<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.MediaStreamTrack <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; <span style="color: #859900;">navigator</span>.getMediaDevices<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">window</span>.MediaStreamTrack <span style="color: #859900;">=</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// 2nd step: verify "getSources" supported which is planned to be removed soon!</span>
<span style="color: #93a1a1;">// "getSources" will be replaced with "getMediaDevices"</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>MediaStreamTrack.getSources<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
MediaStreamTrack.getSources <span style="color: #859900;">=</span> MediaStreamTrack.getMediaDevices;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// todo: need to verify if this trick works</span>
<span style="color: #93a1a1;">// via: https://code.google.com/p/chromium/issues/detail?id=338511</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>MediaStreamTrack.getSources <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; <span style="color: #859900;">navigator</span>.getMediaDevices<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
MediaStreamTrack.getSources <span style="color: #859900;">=</span> <span style="color: #859900;">navigator</span>.getMediaDevices.bind<span style="color: #93a1a1;">(</span><span style="color: #859900;">navigator</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// if still no "getSources"; it MUST be firefox!</span>
<span style="color: #93a1a1;">// or otherwise, it will be older chrome</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>MediaStreamTrack.getSources<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">return</span> callback<span style="color: #93a1a1;">(</span>devicesFetched<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// loop over all audio/video input/output devices</span>
MediaStreamTrack.getSources<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>media_sources)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> sources <span style="color: #859900;">=</span> <span style="color: #268bd2;">[</span><span style="color: #268bd2;">]</span>;
<span style="color: #859900;">for</span> <span style="color: #93a1a1;">(</span><span style="color: #268bd2;">var</span> i <span style="color: #859900;">=</span> <span style="color: #269186;">0</span>; i <span style="color: #859900;">&</span>lt; media_sources.length; i<span style="color: #859900;">++</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
sources.<span style="color: #268bd2;">push</span><span style="color: #93a1a1;">(</span>media_sources<span style="color: #268bd2;">[</span>i<span style="color: #268bd2;">]</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
getAllUserMedias<span style="color: #93a1a1;">(</span>sources<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>callback<span style="color: #93a1a1;">)</span> callback<span style="color: #93a1a1;">(</span>devicesFetched<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">var</span> index <span style="color: #859900;">=</span> <span style="color: #269186;">0</span>;
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> getAllUserMedias(media_sources)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> media_source <span style="color: #859900;">=</span> media_sources<span style="color: #268bd2;">[</span>index<span style="color: #268bd2;">]</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>media_source<span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
<span style="color: #93a1a1;">// to prevent duplicated devices to be fetched.</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>devicesFetched<span style="color: #268bd2;">[</span>media_source.id<span style="color: #268bd2;">]</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
index<span style="color: #859900;">++</span>;
<span style="color: #859900;">return</span> getAllUserMedias<span style="color: #93a1a1;">(</span>media_sources<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
devicesFetched<span style="color: #268bd2;">[</span>media_source.id<span style="color: #268bd2;">]</span> <span style="color: #859900;">=</span> media_source;
index<span style="color: #859900;">++</span>;
getAllUserMedias<span style="color: #93a1a1;">(</span>media_sources<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span></pre>
<br />
You can use it like this:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;">getInputDevices<span style="color: #93a1a1;">(</span><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #93a1a1;">(</span>devices)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">for</span> <span style="color: #93a1a1;">(</span><span style="color: #268bd2;">var</span> device <span style="color: #859900;">in</span> devices<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
device <span style="color: #859900;">=</span> devices<span style="color: #268bd2;">[</span>device<span style="color: #268bd2;">]</span>;
<span style="color: #93a1a1;">// device.kind == 'audio' || 'video'</span>
console<span style="color: #268bd2;">.log</span><span style="color: #93a1a1;">(</span>device.id, device.label<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;</pre>
<h2>
10. How to choose STUN or TURN and skip Host/Local candidates?</h2>
Currently it is not possible for chrome to fetch only STUN or TURN candidates.<br />
<br />
A quick workaround is to skip calling "<code style="font-size: 1.2em;">addIceCandidate</code>" for candidates you wanna skip:<br />
<br />
<pre style="background: rgb(253, 246, 227); color: #586e75; font-family: Consolas, "Lucida Console", "DejaVu Sans Mono", Monaco, "Courier New", monospace; font-size: 0.9333em; font-stretch: normal; line-height: 1.5em; padding: 4px;"><span style="color: #268bd2;">var</span> host <span style="color: #859900;">=</span> <span style="color: #b58900;">false</span>;
<span style="color: #268bd2;">var</span> reflexive <span style="color: #859900;">=</span> <span style="color: #b58900;">false</span>;
<span style="color: #268bd2;">var</span> relay <span style="color: #859900;">=</span> <span style="color: #b58900;">true</span>;
<span style="color: #a57800;"><span style="color: #859900;">peer</span>.onicecandidate <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>(e)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> ice <span style="color: #859900;">=</span> e.candidate;
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>ice<span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span>host <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; ice.candidate.<span style="color: #268bd2;">indexOf</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>typ host <span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">==</span> <span style="color: #859900;">-</span><span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span>reflexive <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; ice.candidate.<span style="color: #268bd2;">indexOf</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>typ srflx <span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">==</span> <span style="color: #859900;">-</span><span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
<span style="color: #859900;">if</span><span style="color: #93a1a1;">(</span>relay <span style="color: #859900;">&</span>amp;<span style="color: #859900;">&</span>amp; ice.candidate.<span style="color: #268bd2;">indexOf</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>typ relay <span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">==</span> <span style="color: #859900;">-</span><span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">return</span>;
POST_to_Other_Peer<span style="color: #93a1a1;">(</span>ice<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>;</pre>
<br />
Above code snippet is taken from <a href="https://www.webrtc-experiment.com/docs/STUN-or-TURN.html">this link</a>.<br />
<br />
<span style="font-size: large;"><b>Updated at Jan 22, 2016</b></span><br />
<br />
You can check tons of other WebRTC tips & tricks at webrtc-pedia page:<br />
<br />
* <a href="https://www.webrtc-experiment.com/webrtcpedia/">https://www.webrtc-experiment.com/webrtcpedia/</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-91158014318111369222013-12-10T11:53:00.000+05:002017-04-28T22:29:26.458+05:00I want to learn WebRTC!<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: x-large;">Are you a newcomer and want to learn <a href="https://www.webrtc-experiment.com/">WebRTC</a>?</span><br />
<br />
First of all; understand following factors involves in each <b>WebRTC</b> application:<br />
<ol>
<li>Signalling servers <span style="font-size: x-small;">(mandatory)</span></li>
<li>ICE servers <span style="font-size: x-small;">(mandatory)</span></li>
<li>Media servers <span style="font-size: x-small;">(optional)</span></li>
<li>JavaScript API (or NATIVE API) <span style="font-size: x-small;">(mandatory)</span></li>
</ol>
<span style="font-size: x-large;">Signalling Servers</span><br />
<br />
E.g. <a href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/socketio-over-nodejs">Socket.io</a>, <a href="https://github.com/muaz-khan/WebRTC-Experiment/tree/master/websocket-over-nodejs">WebSockets</a>, XMPP, SIP or simplest one i.e. <a href="https://github.com/muaz-khan/XHR-Signaling">XHR</a>.<br />
<br />
A gateway or web service which is capable to exchange some kind of data (e.g. text message) among two or more users.<br />
<br />
You can use realtime protocols like <a href="https://en.wikipedia.org/wiki/WebSocket">WebSockets</a> to exchange data as quickly as possible; or simple POST/GET mechanism to POST to server and share with relevant users.<br />
<br />
It is mandatory part of WebRTC; because WebRTC uses Offer/Answer model to setup peer connection. One's offer must be shared with other; and vice versa.<br />
<br />
Quick testers don't need to worry about signalling servers. If you don't know any server side language e.g. node.js or PHP, ASP.NET, Python etc. then you can try 3rd party signalling services like Firebase, PubNub or Pusher. These services are free and provides JavaScript APIs as well.<br />
<br />
<span style="font-size: x-large;">ICE Servers</span><br />
<br />
E.g. STUN or TURN servers.<br />
<br />
WebRTC is bound to use ICE servers because we need to make sure users' Firewalls MUST NOT block UDP or TCP ports.<br />
<br />
A simple ICE server (e.g. STUN) tracks public IP addresses of the NAT. It makes some connectivity checks to fetch working ports e.g. UDP or TCP.<br />
<br />
We need to fallback to relaying ICE server (i.e. TURN) to traverse NATs like symmetric. Such systems hide public ip addresses from external networks; so TURN server comes into action and generates random public ip addresses; and relays incoming stream over private ports of the relevant network.<br />
<br />
Google played a good role to provide public STUN server. You don't need to setup your own ICE server for testing purpose. However, it is suggested to setup custom STUN/TURN servers for commercial uses.<br />
<br />
You can install STUN on Node.js as well. It is too easy to install and use it! However, always keep in mind symmetric NATs!<br />
<br />
<span style="font-size: x-large;">Media Servers</span><br />
<br />
E.g. Asterisk or Freeswitch ... or 3rd party media servers like Kamailio; BigBlueButton etc.<br />
<br />
A service capable to capture and process RTP packets e.g. transcode, record or merge them on the server end.<br />
<br />
A simple media server is capable to handle huge bandwidth comparing a desktop client.<br />
<br />
Media servers comes with stream processing native libraries; same like ffmpeg.<br />
<br />
You can use them for relaying stream over thousand of peers.<br />
<br />
Media Servers are useful in commerical apps; however it is not mandatory. You can interconnect 5 users using peer-to-peer model without worrying about media servers.<br />
<br />
<span style="font-size: x-large;">JavaScript API</span><br />
<br />
E.g. RTCPeerConnection, RTCDataChnannel or SCTP Data Channels, Screen Sharing, Desktop Sharing, etc.<br />
<br />
Your web-app can use WebRTC JavaScript API to setup peer connections.<br />
<br />
You just need to understand offer/answer model.<br />
<br />
<span style="font-size: x-large;">WebRTC Offer/Answer model</span><br />
<br />
Offer/Answer is a well-known signalling model used on the realtime web since last two decades.<br />
<br />
One peer creates offer; and other creates answer.<br />
<br />
Offers/Answers are exchanged using signalling gateways like WebSockets/SIP/XMPP/etc.<br />
<br />
In WebRTC words; offer/answer is a session-description of the user; which contains info like audio/video codecs; system resolutions; bandwidth; cryptographic-keys; media devices; etc.<br />
<br />
If user-A creates offer; his session-description is known as "local session description". When we share his offer with other user; first user's "<b>local session description</b>" becomes "<b>remote session description</b>" for second user.<br />
<br />
So, offer/answer model is a process of exchanging local/remote session descriptions.<br />
<br />
You also need to exchange one's local ICE candidates with other; and vice versa.<br />
<br />
<span style="font-size: x-large;">WebRTC ICE Candidates</span><br />
<br />
Browsers provide API to setup ip/ports that can be used to fetch ICE candidates for the user.<br />
<br />
WebRTC developers can't manually fetch ICE candidates. However, you can easily customize your own ICE server to generate appropriate ports and "public" IP addresses.<br />
<br />
Only signalling section is left to application developer. Media establishment or ICE connectivity checks is performed by the browser itself.<br />
<br />
<span style="font-size: x-large;">Want to learn more about WebRTC?</span><br />
<br />
You can find many tutorials here: <a href="https://www.webrtc-experiment.com/docs/">https://www.webrtc-experiment.com/docs/</a><br />
<br />
If you want to write your first WebRTC application; follow this document:<br />
<br />
<a href="https://www.webrtc-experiment.com/docs/how-to-use-rtcpeerconnection-js-v1.1.html">https://www.webrtc-experiment.com/docs/how-to-use-rtcpeerconnection-js-v1.1.html</a><br />
<br />
This one is useful too:<br />
<br />
<a href="https://www.webrtc-experiment.com/docs/WebRTC-PeerConnection.html">https://www.webrtc-experiment.com/docs/WebRTC-PeerConnection.html</a><br />
<br />
There are some other documents like <a href="https://www.webrtc-experiment.com/docs/webrtc-for-beginners.html">WebRTC for Beginners</a> and <a href="https://www.webrtc-experiment.com/docs/webrtc-for-newbies.html">WebRTC for Newbies</a>.<br />
<br />
<span style="font-size: x-large;">A few things about WebRTC</span><br />
<br />
WebRTC is a realtime communication engine; primarily works in the context of the browser. Though, there are native APIs as well.<br />
<br />
It is not just a collection of API; it an innovative framework capable to define new protocols; suggest new gateways; etc. Remember, WebRTC isn't a service in itself! Services can use WebRTC API to compete the world!<br />
<br />
WebRTC is not a sub-part of browsers. Browsers implemented WebRTC native API to take advantage of its media engines and deliver media/data streaming using WebRTC-standard protocols.<br />
<br />
Browsers usually parse your JavaScript code; execute WebRTC native API accordingly. Then WebRTC native APIs are responsible to setup media connections and handle streaming.<br />
<br />
<b>It is not Google's WebRTC! It is yours!!!</b><br />
<br />
Think about WebRTC as a media streaming engine primarily developed for web developers.<br />
<br />
<span style="font-size: x-large;">Browsers-based JavaScript API</span><br />
<br />
Web browsers exposes following kinds of JavaScript API:<br />
<ol>
<li><b>Media Communication</b> (RTCPeerConnection API)</li>
<li><b>Data Communication</b> (RTCDataChannel/SCTP-datachannel API)</li>
<li><b>Media Capturing</b> (getUserMedia API)</li>
</ol>
There is a separate draft/specification for <b>Media Processing API</b> as well.<br />
<br />
<b>WebAudio API</b> has a separate specification as well; and can be used in WebRTC applications to process/initialize media streams.</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-72227861944106152892012-12-31T13:07:00.000+05:002017-04-28T22:29:46.807+05:00What I did in 2012? A review<div dir="ltr" style="text-align: left;" trbidi="on">
For the sake of fun and keeping record; I'm sharing today "How I spent the year <b>2012</b>!".<br />
<br />
I developed many programming <a href="https://canvas-designer.appspot.com/">tools</a>; experimented many new technologies like <a href="https://webrtc-experiment.appspot.com/">WebRTC</a> as well as <a href="http://muaz-khan.github.com/Everything/Canvas/">Canvas2D</a>; developed many <a href="https://muaz-khan.appspot.com/taxicab/">big</a> private/<a href="https://github.com/muaz-khan/">public</a> projects.<br />
<br />
<span style="font-size: large;">HTML Canvas Designer / <a href="https://canvas-designer.appspot.com/">try yourself</a></span><br />
<a href="https://canvas-designer.appspot.com/">Canvas Designer</a> is a drawing-tool which lets you draw any shape on a single drawing-surface; also it auto-generates appropriate Canvas 2D API relevant code for you in relative/absolute shortened/unshortened formats!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvas-designer.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_oCV4CMX0qppQBt7VRqzB9Vt56SGhGifLId8V7Td7iypzfURk0eE2UsC55EcyvIueSez5ex2UgLry81FIq5hTjRrNI5F60OIsMK1zPtWb4eNTwqpPyTqUA-9EvI2MFBN8izt9a9eoZPGi/s320/HTML5-Canvas-Designer.PNG" width="320" /></a></div>
I designed it in 15-to-20 days....in May 2012.<br />
<br />
<span style="font-size: large;">HTML Curvature / <a href="https://canvature.appspot.com/">try yourself</a></span><br />
<a href="https://canvature.appspot.com/">Curvature</a> is a designer/tool for curving. It generates Canvas2D APIs relevant code in relative/absolute shortened/unshortened formats! It gives you full control over Bezier curves.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvature.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy8mxPmn8DXjEk5lqyrWQaeHUqvKJ56lBnvssU-rbKR5ZZfgqivPIFKe5Y0TDoZDm2Z9WNPeL8jhwd36_wbmdQClVH7bz0TsSfsof7Nwo3_yt8ZHeIdjGB2AKLiW0suhWnChFqOGL2Mb28/s320/Curvature.png" width="320" /></a></div>
It was my first tool in 2012. I started working on it from 1st January; and I release it at 16 January 2012.<br />
<br />
<span style="font-size: large;">WebRTC Experiments & Demos / <a href="https://webrtc-experiment.appspot.com/">try yourself</a></span><br />
In the last quarter of 2012; I started experimenting <a href="http://tools.ietf.org/wg/rtcweb/">RTCWeb</a> APIs. I did many realtime experiments:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/muaz-khan/WebRTC-Experiment" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWEPKCMZAWEP50WjfjgoUkCgGlqiT_N5JgYvO6A6IqJei5HpiukVRNW-8DMc7qHFvNYdntkGe3fGb9xNlz-BUeTlbO3JJ3k0x48epRGHMVGAnIlxEgN6JFjJtPCT8vSvQigtQEQr0HkHW5/s320/WebRTC.png" width="320" /></a></div>
<br />
---) <a href="https://webrtc-experiment.appspot.com/chat/">WebRTC Text Broadcast / Chat using RTCDataChannel APIs</a><br />
---) <a href="https://webrtc-experiment.appspot.com/file-broadcast/">WebRTC File Broadcast / Sharing Files using RTCDataChannel APIs</a><br />
<br />
1) <a href="https://webrtc-experiment.appspot.com/screen-broadcast/">WebRTC Screen Broadcasting!</a><br />
WebRTC screen broadcasting: using Google Chrome experimental <a href="http://www.chromium.org/developers/design-documents/extensions/proposed-changes/apis-under-development/webrtc-tab-content-capture">tabCapture</a> APIs to broadcast screen over many peers.<br />
<br />
2) <a href="https://webrtc-experiment.appspot.com/broadcast/">WebRTC Video Broadcasting!</a><br />
Allows you broadcast your <b>video </b>privately or publicly over many peers.<br />
<br />
3) <a href="https://webrtc-experiment.appspot.com/audio-broadcast/">WebRTC Audio Broadcasting!</a><br />
Allows you broadcast your <b>voice </b>privately or publicly over many peers.<br />
<br />
4) <a href="https://webrtc-experiment.appspot.com/websocket/">WebRTC Experiment using WebSocket!</a><br />
5) <a href="https://webrtc-experiment.appspot.com/javascript/">WebRTC Experiment using Pubnub!</a><br />
6) <a href="https://webrtc-experiment.appspot.com/socket.io/">WebRTC Experiment using socket.io!</a><br />
7) <a href="https://webrtc-experiment.appspot.com/aspnet-mvc/">WebRTC Experiment using XHR and ASP.NET MVC!</a><br />
<br />
and there are many others. You can see all of them <a href="https://webrtc-experiment.appspot.com/">here</a>.<br />
<br />
<span style="font-size: large;">HTML Canvas2D Experiments / <a href="http://muaz-khan.github.com/Everything/Canvas/">try yourself</a></span><br />
I experimented Canvas2D from June to August.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://muaz-khan.github.com/Everything/Canvas/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga32LU0QaVThgXW1HOaVYHhuusbAmySxSlzuydtGm5MAYtUGE3oStLfEvkMei4fpyqppwsXklcAzhkZq0X4Oy2DJnM4ZDPdvexC9bba8vuXIpE78MprVedvWJmHtOgExhUTnSscy9UwlCV/s320/HTML5-Canavs-2D.png" width="320" /></a></div>
<br />
1) <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/">Bezier Curves and Coloring</a> <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-1/">1</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-2/">2</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-3/">3</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-4/">4</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-5/">5</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-6/">6</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-7/">7</a> . <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Bezier-Curves-and-Coloring/Part-8/">8</a><br />
2) <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Dragging-or-Moving-Shapes-smoothly-using-HTML5-Canvas-2D-APIs/">Dragging/Moving shapes smoothly using Canvas 2d APIs</a><br />
3) <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Flying-Bird/">Flying Bird Experiment</a><br />
4) <a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Rocket-Fire/">Rocket Fire Experiment</a><br />
5) <a href="http://muaz-khan.github.com/Everything/Canvas/Sketches/">Many Canvas2D Sketches</a><br />
<br />
I tried <a href="http://muaz-khan.github.com/Everything/CSS3/">CSS3</a> too!<br />
<br />
<span style="font-size: large;">Elance / <a href="https://www.elance.com/s/muazkh/">view my elance profile</a></span><br />
I started working on elance projects from 14th August 2012. Since then I got upto 30 unique clients from different areas of the world! It was a fun! Majority of projects were direct (and private); so you can't see them in the list.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://www.elance.com/s/muazkh/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5q2iTcBKNcNd68Z4qNfFiyoiWcpWSLF-_EIfAuNnW19IJTAFCsJk0tjiS2Qk-qnoTWlpqJbdnPC_2lxglxlJdIohcs60XYm3JVmDZc4H7riNw8EXI0jsasRl9F1u4_9Tmpgqtwb9EGmC2/s320/Muaz-Khan-on-Elance.png" width="320" /></a></div>
<br />
There were some challenging projects for me. I’m a little bit crazy when I accept challenge. I can’t stop trying until I get success. For a project, I tried so many times I can’t remember; but I got success at the end!<br />
<div>
<br />
<br />
<span style="font-size: large;">UseMe: A comparative way of HTML5 features detection /<a href="http://muaz-khan.github.com/UseMe/#Audio/vs/Video"> try yourself</a></span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://muaz-khan.github.com/UseMe/#Audio/vs/Video" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixw6NqMaCYJUXC72xFOG3MAlrehDr6S8rvCF40QAieD3Q469zbtpHIMWIq0YCWVDf0MK1a5chMXduDBN3qPNC_SjbCuIWRHVWYPzNiEbNnyB3laWUdViy-OEdWN8Qux165iuuEd4X1RC_p/s320/UseMe.png" width="320" /></a></div>
<br />
Just a fun!<br />
<br />
<span style="font-size: large;"><a href="https://muaz-khan.appspot.com/taxicab/">Taxicab</a> / work in progress</span><br />
It is an online taxicab dispatching and booking as well as management system...it has following features:<br />
1) An advance online booking system (anyone can book cars)<br />
2) Some unique discounts and offers<br />
3) An advance fares management system<br />
4) Admin panel: manages the whole project<br />
5) Operator panel: dispatches bookings and there are so many other features in this panel<br />
6) An advance search<br />
7) Postcodes database: around 2 million records in that database!!<br />
<br />
and there are so many other things.<br />
You can see my <a href="https://muaz-khan.appspot.com/taxicab/">old taxicab projects</a> (that I developed in 2010) in the apps section below.<br />
<br />
<span style="font-size: large;">Apps / <a href="https://muazkh.appspot.com/">try yourself</a></span><br />
You can see all my apps here:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://muazkh.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXPIfPrZVPjLuQDnrf3VnU7NgcOlVG9ZvE2Ubdx1LK_chq1FGIIDFYNkkCNJc_4WGrhsPNZuLFK7FXJELH3UXRV8Ts1p4aLEalnWk5n7LqwJuPkEQNAiE03ikPhSFn_x7X4ThHt5loobtM/s320/Muaz-Khan-Apps.png" width="320" /></a></div>
<br />
<br />
<span style="font-size: large;">Dashing Quill Blog / <a href="http://dashingquill.wordpress.com/">another blog</a></span><br />
I started this blog in 2011. In 2012, upto 7,600 unique people visited:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://dashingquill.wordpress.com/2012/12/31/2012-in-review/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9G6ufkxmEnwHe4gpmDwz7CAYHn4_5R4PaTK1Ts7rq8gK7nnB8KcVxQbYpT2vYsVmMILjUW3D2_j9-o6NH_Oy2WdejuODQz3KqN-Aa67EPY7Gyorq4rDW0Zqx5vVdc2mJIkjwxC9Mx-P2f/s320/Dashing+Quill+Blog.png" width="320" /></a></div>
<br />
<a href="http://dashingquill.wordpress.com/2012/annual-report/">Click here to see full report of the year 2012 for Dashing Quill Blog</a><br />
You can see what I posted there in 2012 <a href="http://dashingquill.wordpress.com/2012/">here</a>.<br />
<br />
<b>Most famous posts were:</b><br />
1) Capturing WebCam using DirectShow.NET Library - <a href="http://dashingquill.wordpress.com/2012/06/27/capturing-webcam-using-directshow-net-library/">Link</a><br />
2) Javascript and CSS3 only sliding up/down transition effects - <a href="http://dashingquill.wordpress.com/2011/08/16/javascript-and-css3-only-sliding-updown-transition-effects/">Link</a><br />
3) Handling errors in ASP.NET MVC - <a href="http://dashingquill.wordpress.com/2011/01/26/handle-any-kind-of-errorexception-in-asp-net-mvc/">Link</a><br />
<br />
<span style="font-size: large;">Google Sites / <a href="https://sites.google.com/site/muazkh/">another blog</a></span><br />
I posted two most famous articles in this blog...<br />
<br />
1) ASP.NET MVC security and hacking: Defense-in-depth - <a href="https://sites.google.com/site/muazkh/asp-net-mvc-security-and-hacking-defense-in-depth">Link</a><br />
2) JavaScript Logical Operators: An Overview - <a href="https://sites.google.com/site/muazkh/javascript-logical-operators-an-overview">Link</a><br />
<br />
<span style="font-size: large;">Note:</span><br />
Upto 24655 unique people visited <a href="http://muaz-khan.blogspot.com/">this blog</a> in 2012.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-8qGvh8H-guFDI2JBthm7qNOfgZF5FpZ7gttJQVhJhKaw_JqwXO98T4lay_r9fGkuHtPHH3rRsmVXh8KOWUWB7PE6sw3yqe6ZHtcmzgc8zJxP1Twe6t7gDO4nytpecwmrcRjSh-mZZalT/s1600/Muaz+Khan+Blog+Stats.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-8qGvh8H-guFDI2JBthm7qNOfgZF5FpZ7gttJQVhJhKaw_JqwXO98T4lay_r9fGkuHtPHH3rRsmVXh8KOWUWB7PE6sw3yqe6ZHtcmzgc8zJxP1Twe6t7gDO4nytpecwmrcRjSh-mZZalT/s320/Muaz+Khan+Blog+Stats.png" width="320" /></a></div>
<br />
The most famous posts were:<br />
1) <a href="http://muaz-khan.blogspot.com/2012/02/absolute-or-relative-url-issues-and.html">Absolute or Relative URL issues and the solution</a><br />
2) <a href="http://muaz-khan.blogspot.com/2012/06/exploring-csp-content-security-policy.html">Exploring CSP (Content Security Policy) using ASP.NET MVC</a><br />
3) <a href="http://muaz-khan.blogspot.com/2011/09/wiki-markup-implementation-in-c.html">C# and wiki markup parsing</a><br />
<br />
<span style="font-size: large;"><a href="http://dtweet.codeplex.com/">DTweet</a> / an open source ASP.NET MVC project</span><br />
It is an open source project. I developed it in 2011 and released in 2012.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhccnC96nCQeY95ZvXItfSbwbS4U2T0Dpzk_5uVxwDMlXcn855f52vr3VHBpHLBPtDU7RieNsiC5Xip1CM38V3H-VLeb1a0C4JFZFgbKxKoBrnM_dSEhfK1dqcPALkK0fe1Wf4tDAK6GBYU/s1600/DTweet.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="164" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhccnC96nCQeY95ZvXItfSbwbS4U2T0Dpzk_5uVxwDMlXcn855f52vr3VHBpHLBPtDU7RieNsiC5Xip1CM38V3H-VLeb1a0C4JFZFgbKxKoBrnM_dSEhfK1dqcPALkK0fe1Wf4tDAK6GBYU/s320/DTweet.png" width="320" /></a></div>
<br />
In 2012, I started my career as open-source web developer. <b>2013</b> is coming with great opportunities.<br />
<div>
<br /></div>
<div>
<span style="font-size: large;">I’m ready to do better! </span><br />
<h1 class="title entry-title" itemprop="name" style="background-color: white; color: #333333; display: table-cell; font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 20px; font-weight: normal; margin: 0px; padding: 0px 40px 0px 0px; position: relative; text-align: center; vertical-align: middle; width: 710px;">
</h1>
<h1 class="title entry-title" itemprop="name" style="background-color: white; color: #333333; display: table-cell; font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 20px; font-weight: normal; margin: 0px; padding: 0px 40px 0px 0px; position: relative; text-align: left; vertical-align: middle; width: 710px;">
</h1>
</div>
</div>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-41950607424813001772012-10-07T11:46:00.001+05:002017-12-02T09:52:31.301+05:00Save files on disk using JavaScript or JQuery!<div dir="ltr" style="text-align: left;" trbidi="on">
You can save any file, or <b>DataURL</b>, or <b>Blob</b> on disk using HTML5's newly introduced "<b>download</b>" attribute.<br />
<br />
<span style="font-size: large;">Use cases:</span><br />
<br />
1. Force browser to download/save files like PDF/HTML/PHP/ASPX/JS/CSS/etc. on disk<br />
2. Concatenate all transmitted blobs and save them as file on disk - it is useful in file sharing applications<br />
<br />
<b>Microsoft Edge? (msSaveBlob/msSaveOrOpenBlob)</b> <a href="https://msdn.microsoft.com/en-us/library/hh779016(v=vs.85).aspx">https://msdn.microsoft.com/en-us/library/hh779016(v=vs.85).aspx</a><br />
<br />
<pre style="background: #fdf6e3; color: #586e75;"><span style="color: #93a1a1;">/**
* @param {Blob} file - File or Blob object. This parameter is required.
* @param {string} fileName - Optional file name e.g. "image.png"
*/</span>
<span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">invokeSaveAsDialog</span>(file, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>file<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">throw</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>Blob object is required.<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">}</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span>file.type<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">try</span> <span style="color: #268bd2;">{</span>
file.type <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>video/webm<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">catch</span> <span style="color: #93a1a1;">(</span>e<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span><span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">var</span> fileExtension <span style="color: #859900;">=</span> <span style="color: #93a1a1;">(</span>file.type <span style="color: #859900;">||</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>video/webm<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>.<span style="color: #268bd2;">split</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>/<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span><span style="color: #268bd2;">[</span><span style="color: #269186;">1</span><span style="color: #268bd2;">]</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span>fileName <span style="color: #859900;">&</span><span style="color: #859900;">&</span> fileName.<span style="color: #268bd2;">indexOf</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>.<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">!</span><span style="color: #859900;">==</span> <span style="color: #859900;">-</span><span style="color: #269186;">1</span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> splitted <span style="color: #859900;">=</span> fileName.<span style="color: #268bd2;">split</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>.<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
fileName <span style="color: #859900;">=</span> splitted<span style="color: #268bd2;">[</span><span style="color: #269186;">0</span><span style="color: #268bd2;">]</span>;
fileExtension <span style="color: #859900;">=</span> splitted<span style="color: #268bd2;">[</span><span style="color: #269186;">1</span><span style="color: #268bd2;">]</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">var</span> fileFullName <span style="color: #859900;">=</span> <span style="color: #93a1a1;">(</span>fileName <span style="color: #859900;">||</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">Math</span>.<span style="color: #268bd2;">round</span><span style="color: #93a1a1;">(</span><span style="color: #859900;">Math</span>.<span style="color: #268bd2;">random</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">*</span> <span style="color: #269186;">9999999999</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">+</span> <span style="color: #269186;">888888888</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">)</span> <span style="color: #859900;">+</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>.<span style="color: #c60000;">'</span></span> <span style="color: #859900;">+</span> fileExtension;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> <span style="color: #859900;">navigator</span>.msSaveOrOpenBlob <span style="color: #859900;">!</span><span style="color: #859900;">==</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>undefined<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">return</span> <span style="color: #859900;">navigator</span>.msSaveOrOpenBlob<span style="color: #93a1a1;">(</span>file, fileFullName<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> <span style="color: #859900;">navigator</span>.msSaveBlob <span style="color: #859900;">!</span><span style="color: #859900;">==</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>undefined<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #859900;">return</span> <span style="color: #859900;">navigator</span>.msSaveBlob<span style="color: #93a1a1;">(</span>file, fileFullName<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">var</span> hyperlink <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
hyperlink.href <span style="color: #859900;">=</span> URL.createObjectURL<span style="color: #93a1a1;">(</span>file<span style="color: #93a1a1;">)</span>;
hyperlink.download <span style="color: #859900;">=</span> fileFullName;
hyperlink.style <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>display:none;opacity:0;color:transparent;<span style="color: #c60000;">'</span></span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">document</span>.body <span style="color: #859900;">||</span> <span style="color: #859900;">document</span>.documentElement<span style="color: #93a1a1;">)</span>.<span style="color: #a57800;">appendChild</span><span style="color: #93a1a1;">(</span>hyperlink<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> hyperlink.click <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
hyperlink.<span style="color: #a57800;">click</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
hyperlink.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
hyperlink.dispatchEvent<span style="color: #93a1a1;">(</span><span style="color: #859900;">new</span> MouseEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #268bd2;">{</span>
view: <span style="color: #859900;">window</span>,
bubbles: <span style="color: #b58900;">true</span>,
cancelable: <span style="color: #b58900;">true</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
(window.URL || window.webkitURL).revokeObjectURL<span style="color: #93a1a1;">(</span>hyperlink.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
</pre>
<br />
Here is how to use above function:<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;"><span style="color: #268bd2;">var</span> textFile <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> Blob<span style="color: #93a1a1;">(</span><span style="color: #268bd2;">[</span><span style="color: #269186;"><span style="color: #c60000;">'</span>Hello Sir<span style="color: #c60000;">'</span></span><span style="color: #268bd2;">]</span>, <span style="color: #268bd2;">{</span>
type: <span style="color: #269186;"><span style="color: #c60000;">'</span>text/plain<span style="color: #c60000;">'</span></span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span>;
invokeSaveAsDialog<span style="color: #93a1a1;">(</span>textFile, <span style="color: #269186;"><span style="color: #c60000;">'</span>TextFile.txt<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
</pre>
<br />
You can pass two arguments over "<b>SaveToDisk</b>" function:<br />
<br />
1. file-URL or blob or data-URL - it is <b>mandatory</b><br />
2. file name - it is <b>optional</b><br />
<br />
Here is "<b>SaveToDisk</b>" function uses <u>new syntax</u> of "<b>createEvent</b>" API:<br />
<br />
<pre style="background: rgb(253, 246, 227);"><span style="color: #586e75;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">SaveToDisk</span>(fileURL, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// for non-IE</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.ActiveXObject<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> save <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
save.href <span style="color: #859900;">=</span> fileURL;
save.download <span style="color: #859900;">=</span> fileName <span style="color: #859900;">||</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>unknown<span style="color: #c60000;">'</span></span>;
save.style <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>display:none;opacity:0;color:transparent;<span style="color: #c60000;">'</span></span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">document</span>.body <span style="color: #859900;">||</span> <span style="color: #859900;">document</span>.documentElement<span style="color: #93a1a1;">)</span>.<span style="color: #a57800;">appendChild</span><span style="color: #93a1a1;">(</span>save<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> save.click <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
save.<span style="color: #a57800;">click</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
save.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">var</span> <span style="color: #859900;">event</span> <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.createEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>Event<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">event</span>.initEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #b58900;">true</span>, <span style="color: #b58900;">true</span><span style="color: #93a1a1;">)</span>;
save.dispatchEvent<span style="color: #93a1a1;">(</span><span style="color: #859900;">event</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">(</span><span style="color: #859900;">window</span>.URL <span style="color: #859900;">||</span> <span style="color: #859900;">window</span>.webkitURL<span style="color: #93a1a1;">)</span>.revokeObjectURL<span style="color: #93a1a1;">(</span>save.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// for IE</span>
<span style="color: #859900;">else</span> <span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.ActiveXObject </span><span style="color: #859900;">&&</span><span style="color: #586e75;"> </span><span style="color: #859900;">document</span><span style="color: #586e75;">.execCommand</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;"> </span><span style="color: #268bd2;">{</span><span style="color: #586e75;">
</span><span style="color: #268bd2;">var</span><span style="color: #586e75;"> _window </span><span style="color: #859900;">=</span><span style="color: #586e75;"> </span><span style="color: #859900;">window</span><span style="color: #586e75;">.</span><span style="color: #a57800;">open</span><span style="color: #93a1a1;">(</span><span style="color: #586e75;">fileURL, </span><span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
_window.</span><span style="color: #859900;">document</span><span style="color: #586e75;">.</span><span style="color: #a57800;">close</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
_window.</span><span style="color: #859900;">document</span><span style="color: #586e75;">.</span><span style="color: #268bd2;">execCommand</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>SaveAs<span style="color: #c60000;">'</span></span><span style="color: #586e75;">, </span><span style="color: #b58900;">true</span><span style="color: #586e75;">, fileName </span><span style="color: #859900;">||</span><span style="color: #586e75;"> fileURL</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">
_window.</span><span style="color: #a57800;">close</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
</span><span style="color: #268bd2;">}</span><span style="color: #586e75;">
</span><span style="color: #268bd2;">}</span><span style="color: #586e75;">
</span></pre>
<br />
Here is "<b>SaveToDisk</b>" function uses <u>old syntax</u> of "<b>createEvent</b>" API:<br />
<br />
<pre style="background: rgb(253, 246, 227);"><span style="color: #586e75;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">SaveToDisk</span>(fileURL, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #93a1a1;">// for non-IE</span>
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">!</span><span style="color: #859900;">window</span>.ActiveXObject<span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> save <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
save.href <span style="color: #859900;">=</span> fileURL;
save.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
save.download <span style="color: #859900;">=</span> fileName <span style="color: #859900;">||</span> fileURL;
<span style="color: #268bd2;">var</span> evt <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.createEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>MouseEvents<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
evt.initMouseEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #b58900;">true</span>, <span style="color: #b58900;">true</span>, <span style="color: #859900;">window</span>, <span style="color: #269186;">1</span>, <span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>, <span style="color: #269186;">0</span>,
<span style="color: #b58900;">false</span>, <span style="color: #b58900;">false</span>, <span style="color: #b58900;">false</span>, <span style="color: #b58900;">false</span>, <span style="color: #269186;">0</span>, <span style="color: #b58900;">null</span><span style="color: #93a1a1;">)</span>;
save.dispatchEvent<span style="color: #93a1a1;">(</span>evt<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">window</span>.URL <span style="color: #859900;">||</span> <span style="color: #859900;">window</span>.webkitURL<span style="color: #93a1a1;">)</span>.revokeObjectURL<span style="color: #93a1a1;">(</span>save.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">// for IE</span>
<span style="color: #859900;">else</span> <span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span> <span style="color: #859900;">!</span><span style="color: #859900;">!</span> <span style="color: #859900;">window</span>.ActiveXObject </span><span style="color: #859900;">&&</span><span style="color: #586e75;"> </span><span style="color: #859900;">document</span><span style="color: #586e75;">.execCommand</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;"> </span><span style="color: #268bd2;">{</span><span style="color: #586e75;">
</span><span style="color: #268bd2;">var</span><span style="color: #586e75;"> _window </span><span style="color: #859900;">=</span><span style="color: #586e75;"> </span><span style="color: #859900;">window</span><span style="color: #586e75;">.</span><span style="color: #a57800;">open</span><span style="color: #93a1a1;">(</span><span style="color: #586e75;">fileURL, </span><span style="color: #269186;"><span style="color: #c60000;">"</span>_blank<span style="color: #c60000;">"</span></span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
_window.</span><span style="color: #859900;">document</span><span style="color: #586e75;">.</span><span style="color: #a57800;">close</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
_window.</span><span style="color: #859900;">document</span><span style="color: #586e75;">.</span><span style="color: #268bd2;">execCommand</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>SaveAs<span style="color: #c60000;">'</span></span><span style="color: #586e75;">, </span><span style="color: #b58900;">true</span><span style="color: #586e75;">, fileName </span><span style="color: #859900;">||</span><span style="color: #586e75;"> fileURL</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">
_window.</span><span style="color: #a57800;">close</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span><span style="color: #586e75;">;
</span><span style="color: #268bd2;">}</span><span style="color: #586e75;">
</span><span style="color: #268bd2;">}</span><span style="color: #586e75;">
</span></pre>
<br />
You can use FileReader too, to save "Blob" on disk:<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">SaveToDisk</span>(blobURL, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> reader <span style="color: #859900;">=</span> <span style="color: #859900;">new</span> FileReader<span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
reader.readAsDataURL<span style="color: #93a1a1;">(</span>blobURL<span style="color: #93a1a1;">)</span>;
<span style="color: #a57800;"><span style="color: #859900;">reader</span>.<span style="color: #a57800;">onload</span> <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>(event)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> save <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
save.href <span style="color: #859900;">=</span> <span style="color: #859900;">event</span>.target.result;
save.download <span style="color: #859900;">=</span> fileName <span style="color: #859900;">||</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>unknown file<span style="color: #c60000;">'</span></span>;
save.style <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>display:none;opacity:0;color:transparent;<span style="color: #c60000;">'</span></span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">document</span>.body <span style="color: #859900;">||</span> <span style="color: #859900;">document</span>.documentElement<span style="color: #93a1a1;">)</span>.<span style="color: #a57800;">appendChild</span><span style="color: #93a1a1;">(</span>save<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> save.click <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
save.<span style="color: #a57800;">click</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
save.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
<span style="color: #268bd2;">var</span> <span style="color: #859900;">event</span> <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.createEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>Event<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">event</span>.initEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #b58900;">true</span>, <span style="color: #b58900;">true</span><span style="color: #93a1a1;">)</span>;
save.dispatchEvent<span style="color: #93a1a1;">(</span><span style="color: #859900;">event</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">(</span><span style="color: #859900;">window</span>.URL <span style="color: #859900;">||</span> <span style="color: #859900;">window</span>.webkitURL<span style="color: #93a1a1;">)</span>.revokeObjectURL<span style="color: #93a1a1;">(</span>save.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>;
<span style="color: #268bd2;">}</span>
</pre>
<br />
Using "<b>SaveToDisk</b>" function<br />
<br />
Force an image to be saved on disk instead of rendered by the browser:<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;">SaveToDisk<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>https://muazkh.appspot.com/images/Curvature.PNG<span style="color: #c60000;">'</span></span>, <span style="color: #269186;"><span style="color: #c60000;">'</span>image.png<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
</pre>
<br />
Force downloading of pdf files (it will NEVER allow any browser specific pdf-reader to render/open your pdf files):<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;">SaveToDisk<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>https://muazkh.googlecode.com/files/Muaz-Khan-CV.pdf<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
</pre>
<br />
Even you can enforce following web-browser specific files to be downloaded/saved on the disk instead of rendered within the browser!<br />
<br />
<table style="width: 100%;"><tbody>
<tr> <td><strong>JavaScript</strong></td> <td><code>SaveToDisk('/javascript-file.js')</code></td> </tr>
<tr> <td><strong>HTML</strong></td> <td><code>SaveToDisk('/html-file.html')</code></td> </tr>
<tr> <td><strong>CSS</strong></td> <td><code>SaveToDisk('/css-file.css')</code></td> </tr>
<tr> <td><strong>ASPX</strong></td> <td><code>SaveToDisk('/aspx-file.aspx')</code></td> </tr>
<tr> <td><strong>PHP</strong></td> <td><code>SaveToDisk('/php-file.php')</code></td> </tr>
<tr> <td><strong>MVC</strong></td> <td><code>SaveToDisk('/controller/action-method')</code></td> </tr>
</tbody></table>
<br />
Following apps are using "SaveToDisk" function to save files/blobs/etc. on disk:<br />
<br />
<table style="width: 100%;"><tbody>
<tr> <td><a href="https://github.com/muaz-khan/DataChannel">DataChannel.js</a></td> <td>A library for realtime data/file sharing using WebRTC!</td> </tr>
<tr> <td><a href="https://github.com/muaz-khan/RTCMultiConnection">RTCMultiConnection.js</a></td> <td>A library for realtime audio/video/screen/data and file sharing using WebRTC!</td> </tr>
<tr> <td><a href="https://rtcmulticonnection.herokuapp.com/demos/file-sharing.html">Group File Sharing</a></td> <td>Sharing files over multiple peer connections concurrently</td> </tr>
<tr> <td><a href="https://www.webrtc-experiment.com/RecordRTC/">RecordRTC</a></td> <td>A library for WebRTC-Developers to record audio and video streams</td> </tr>
</tbody></table>
<br />
<span style="font-size: large;">Specification</span><br />
<br />
1. https://developer.mozilla.org/en-US/docs/DOM/document.createEvent<br />
2. http://www.w3.org/TR/DOM-Level-3-Events/#events-Events-DocumentEvent-createEvent<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">SaveToDisk</span>(fileUrl, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> hyperlink <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
hyperlink.href <span style="color: #859900;">=</span> fileUrl;
hyperlink.download <span style="color: #859900;">=</span> fileName <span style="color: #859900;">||</span> fileUrl;
hyperlink.style <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>display:none;opacity:0;color:transparent;<span style="color: #c60000;">'</span></span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">document</span>.body <span style="color: #859900;">||</span> <span style="color: #859900;">document</span>.documentElement<span style="color: #93a1a1;">)</span>.<span style="color: #a57800;">appendChild</span><span style="color: #93a1a1;">(</span>hyperlink<span style="color: #93a1a1;">)</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> hyperlink.click <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
hyperlink.<span style="color: #a57800;">click</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
hyperlink.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
hyperlink.dispatchEvent<span style="color: #93a1a1;">(</span><span style="color: #859900;">new</span> MouseEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #268bd2;">{</span>
view: <span style="color: #859900;">window</span>,
bubbles: <span style="color: #b58900;">true</span>,
cancelable: <span style="color: #b58900;">true</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #93a1a1;">(</span><span style="color: #859900;">window</span>.URL <span style="color: #859900;">||</span> <span style="color: #859900;">window</span>.webkitURL<span style="color: #93a1a1;">)</span>.revokeObjectURL<span style="color: #93a1a1;">(</span>hyperlink.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
</pre>
<br />
Remember, "<b>SaveToDisk</b>" works fine on Firefox <u>nightly</u> and <u>aurora</u>.<br />
<br />
To support Firefox general release; use same workaround that is used for IE in this post (with a little bit changes).<br />
<br />
<span style="font-size: large;">If Firefox fails:</span><br />
<br />
It seems that firefox doesn't allows dispatching (click) event handlers if HYPER-LINK element is NOT in the DOM-tree.<br />
<br />
<i>Don't call "<b>revokeObjectURL</b>" outside the "<b>onclick</b>" handler.</i><br />
<br />
You simply need to append HYPER-LINK element into DOM; and remove it after "onclick" is fired:<br />
<br />
<pre style="background: #fdf6e3; color: #586e75;"><span style="color: #a57800;"><span style="color: #268bd2;">function</span> <span style="color: #a57800;">SaveToDisk</span>(fileUrl, fileName)</span> <span style="color: #268bd2;">{</span>
<span style="color: #268bd2;">var</span> hyperlink <span style="color: #859900;">=</span> <span style="color: #859900;">document</span>.<span style="color: #a57800;">createElement</span><span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>a<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span>;
hyperlink.href <span style="color: #859900;">=</span> fileUrl;
hyperlink.download <span style="color: #859900;">=</span> fileName <span style="color: #859900;">||</span> fileUrl;
hyperlink.style <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>display:none;opacity:0;color:transparent;<span style="color: #c60000;">'</span></span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">document</span>.body <span style="color: #859900;">||</span> <span style="color: #859900;">document</span>.documentElement<span style="color: #93a1a1;">)</span>.<span style="color: #a57800;">appendChild</span><span style="color: #93a1a1;">(</span>hyperlink<span style="color: #93a1a1;">)</span>;
<span style="color: #a57800;"><span style="color: #859900;">hyperlink</span>.<span style="color: #a57800;">onclick</span> <span style="color: #859900;">=</span> <span style="color: #268bd2;">function</span>()</span> <span style="color: #268bd2;">{</span>
hyperlink.parentNode.<span style="color: #a57800;">removeChild</span><span style="color: #93a1a1;">(</span>hyperlink<span style="color: #93a1a1;">)</span>;
<span style="color: #93a1a1;">(</span><span style="color: #859900;">window</span>.URL <span style="color: #859900;">||</span> <span style="color: #859900;">window</span>.webkitURL<span style="color: #93a1a1;">)</span>.revokeObjectURL<span style="color: #93a1a1;">(</span>hyperlink.href<span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>;
<span style="color: #859900;">if</span> <span style="color: #93a1a1;">(</span><span style="color: #859900;">typeof</span> hyperlink.click <span style="color: #859900;">===</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>function<span style="color: #c60000;">'</span></span><span style="color: #93a1a1;">)</span> <span style="color: #268bd2;">{</span>
hyperlink.<span style="color: #a57800;">click</span><span style="color: #93a1a1;">(</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span> <span style="color: #859900;">else</span> <span style="color: #268bd2;">{</span>
hyperlink.target <span style="color: #859900;">=</span> <span style="color: #269186;"><span style="color: #c60000;">'</span>_blank<span style="color: #c60000;">'</span></span>;
hyperlink.dispatchEvent<span style="color: #93a1a1;">(</span><span style="color: #859900;">new</span> MouseEvent<span style="color: #93a1a1;">(</span><span style="color: #269186;"><span style="color: #c60000;">'</span>click<span style="color: #c60000;">'</span></span>, <span style="color: #268bd2;">{</span>
view: <span style="color: #859900;">window</span>,
bubbles: <span style="color: #b58900;">true</span>,
cancelable: <span style="color: #b58900;">true</span>
<span style="color: #268bd2;">}</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">)</span>;
<span style="color: #268bd2;">}</span>
<span style="color: #268bd2;">}</span>
</pre>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-37421428543830440112012-06-20T08:43:00.001+05:002017-04-28T22:30:56.267+05:00Exploring CSP (Content Security Policy) using ASP.NET MVC<div dir="ltr" style="text-align: left;" trbidi="on">
In this post, I'll practically show you one promising new defense using CSP (Content Security Policy) that can significantly reduce possible XSS (Cross-Site Scripting) attacks in modern web-browsers (currently Chrome 16+ and Firefox 4+)!<br />
<div>
<br />
I'll not only block inline/outer scripts but also ask user-agent to auto-generate report for each violated activity it founds that violates my pre-defined policy.<br />
<br />
I created an <strong>ASP.NET MVC</strong> demo app; which is aimed to:<br />
<ol>
<li>Attach HTTP headers for <abbr title="Content Security Policy">CSP</abbr> with each response</li>
<li>Save and preview all reports generated by the browser</li>
</ol>
<div>
<span style="font-size: x-large; text-align: center;"> </span><a href="http://webrtcweb.com/old/CSPProject.zip" style="font-size: xx-large; text-align: center;">Download Demo Project</a> </div>
<div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibTpyPgTu99wC3Ivdryso3a7qWSxCihdiBA6U8vtKzreE0n5CfNzO6_Z6iACzfLCu5zMbg5rzTftVMmUcevd0AtSF-7ybyVKLpGKxj-FUThhTmg5QdFeSP4nubU06RAKZQzPBe5xyBkBuO/s1600/Content+Security+Policy+(CSP).png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibTpyPgTu99wC3Ivdryso3a7qWSxCihdiBA6U8vtKzreE0n5CfNzO6_Z6iACzfLCu5zMbg5rzTftVMmUcevd0AtSF-7ybyVKLpGKxj-FUThhTmg5QdFeSP4nubU06RAKZQzPBe5xyBkBuO/s640/Content+Security+Policy+(CSP).png" width="640" /></a></div>
<br /></div>
<br />
Here is how <strong>Google Chrome</strong> blocks unallowed scripts:<br />
<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipnXE8_xDpECnRClxBe6yFKDK1Q5D6roFa4M4o0OKKcLANvT-2Q1Zi3FRF3JpXxx1tspUrumkTb78x6HzKyQ0-C9QV0JXexHrbbZyRn62PSPrlk6DquvKahtm5PSE5HJ9Q3MZtvraTbucP/s1600/Content-Security-Policy-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="40" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipnXE8_xDpECnRClxBe6yFKDK1Q5D6roFa4M4o0OKKcLANvT-2Q1Zi3FRF3JpXxx1tspUrumkTb78x6HzKyQ0-C9QV0JXexHrbbZyRn62PSPrlk6DquvKahtm5PSE5HJ9Q3MZtvraTbucP/s320/Content-Security-Policy-2.png" width="320" /></a></div>
<br /></div>
<br />
And the <strong>Firefox</strong> is blocking unallowed scripts as well:<br />
<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEnTzWYme_W7KsDknTvrWtAixe_46XH6LIVPTsbcHrWuYGqIuKgGeSOwVuCBFRG9Q7jGQhhyKGd1CoTlAIOSp68kHHLQQ0ICgpBjHI8oyLpgsLAuY-yfu8yLd4QQlQ0j9CFv_lOL-1VHI2/s1600/Content-Security-Policy-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEnTzWYme_W7KsDknTvrWtAixe_46XH6LIVPTsbcHrWuYGqIuKgGeSOwVuCBFRG9Q7jGQhhyKGd1CoTlAIOSp68kHHLQQ0ICgpBjHI8oyLpgsLAuY-yfu8yLd4QQlQ0j9CFv_lOL-1VHI2/s320/Content-Security-Policy-3.png" width="320" /></a></div>
<br /></div>
Here is how <strong>Google Chrome</strong> generates a report to pre-defined '<tt>report-uri</tt>' directive and passes data as '<tt>application/json</tt>':<br />
<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh11R10Kpnya58hlp3DH2DyER7q0avxDF-TtVrOLcMHJfuMpL4nowMBKjI3ineo9dxcD_OFwAEHo_Z4XTqlWC8EMgbNb2QCpcAZyflpDbUJT-n-XLp7J0zvOAaS9ivvnsj48C6Az6VvYJB-/s1600/Content-Security-Policy-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh11R10Kpnya58hlp3DH2DyER7q0avxDF-TtVrOLcMHJfuMpL4nowMBKjI3ineo9dxcD_OFwAEHo_Z4XTqlWC8EMgbNb2QCpcAZyflpDbUJT-n-XLp7J0zvOAaS9ivvnsj48C6Az6VvYJB-/s320/Content-Security-Policy-4.png" width="320" /></a></div>
<br /></div>
<br />
To append <abbr title="Content Security Policy">CSP</abbr> headers for each response; we'll add code in "<tt>Application_BeginRequest</tt>" method in "<tt>MvcApplication</tt>" class under "<tt>Global.asax</tt>" file: <br />
<br />
<pre>void Application_BeginRequest(object sender, EventArgs ex)
{
/* ======== For Chrome ======== */
Response.AddHeader("X-WebKit-CSP", "default-src 'self'; img-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri /Home/Report");
/* ======== For Firefox ======== */
Response.AddHeader("X-Content-Security-Policy", "default-src 'self'; img-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri /Home/Report");
}
</pre>
<br />
<div style="text-align: center;">
<span style="font-size: x-large;"> <a href="http://webrtcweb.com/old/CSPProject.zip">Download Demo Project</a></span></div>
<br />
As you can see in the above code, I used following two headers for <abbr title="Content Security Policy">CSP</abbr> implementation:<br />
<ol>
<li>"<tt>X-Content-Security-Policy</tt>" for <strong>Firefox</strong></li>
<li>"<tt>X-WebKit-CSP</tt>" for <strong>Chrome</strong></li>
</ol>
I used '<tt>report-uri</tt>' directive to instruct the browser to notify me about any activity that violates my pre-defined policies.<br />
<br />
<pre>... report-uri /Home/Report
</pre>
<pre></pre>
The "<tt>Report</tt>" action method looks like this:<br />
<br />
<pre>public JsonResult Report() { return Json(true); }
</pre>
<pre></pre>
Remember, POST/GET doesn't matter for this action method! Also the browser will totally ignore what you return with this action. Because: <em>"User Agents MUST NOT honor HTTP 3xx response codes to prevent HTTP header leakage across domains."</em><br />
<em><br />
</em><br />
I'm blocking all inline as well as outer scripts for possible security gain; however I'm allowing inline-styles because it is difficult for me to not use inline-styles!!!<br />
<br />
<pre>...; script-src 'self'; style-src 'self' 'unsafe-inline'; ...
</pre>
<pre></pre>
<em>"Banning inline script is the biggest security win CSP provides, and banning inline style likewise hardens your application."</em><br />
<em><br />
</em><br />
To allow all outer scripts from SSL:<br />
<br />
<pre>script-src https:
</pre>
<pre></pre>
To allow inline scripts as well:<br />
<br />
<pre>script-src https: 'unsafe-inline'
</pre>
<pre></pre>
Instead of blocking any resource; you can use report-only header to ONLY get notified of violated activities:<br />
<pre></pre>
<pre>/* ======== For Chrome ======== */
Response.AddHeader("X-WebKit-CSP-Report-Only", "...; report-uri /Home/Report");
/* ======== For Firefox ======== */
Response.AddHeader("X-Content-Security-Policy-Report-Only", "...; report-uri /Home/Report");
</pre>
<pre></pre>
<em>"The policy specified in report-only mode won't block restricted resources, but it will send violation reports to the location you specify."</em><br />
<br />
<em><br />
</em><br />
<pre>img-src *
</pre>
<pre></pre>
I'm also allowing images from all locations (http/https) by using asterisk (*) symbol. To restrict SSL only domains for images:<br />
<br />
<pre>img-src https: *
</pre>
<pre></pre>
Or to allow self-domain ONLY for images:<br />
<br />
<pre>img-src 'self'
</pre>
<pre></pre>
There as so many other directives that you can use to get full control over content that was not possible yesterday. For example, you can use:<br />
<ol>
<li><br />
<tt>connect-src</tt><br />
Limits the origins to which you can connect (via XHR, WebSockets, and EventSource).<br />
</li>
<li><br />
<tt>font-src</tt><br />
Pecifies the origins that can serve web fonts. Google's Web Fonts could be enabled via font-src https://themes.googleusercontent.com<br />
</li>
<li><br />
<tt>frame-src</tt><br />
Lists the origins that can be embedded as frames. For example: frame-src https://youtube.com would enable embedding YouTube videos, but no other origins.<br />
</li>
<li><br />
<tt>media-src</tt><br />
Restricts the origins allowed to deliver video and audio.<br />
</li>
<li><br />
<tt>object-src</tt><br />
Allows control over Flash and other plugins.<br />
</li>
</ol>
<h2>
Firefox's Behavior regarding CSP</h2>
<strong>Firefox</strong> fails to recognize/parse '<tt>unsafe-inline</tt>' source as show in the following figure:<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCZ2JM_ca81W97fTwq_7flnWasreXaUgq5vfdy4mNTD3Mg4SoVQguuS9u9-IOJNCmCKJSFmqc8R_d_glzdy_NVdM513sGEYCj7q1g5o20YULbWrSu9_t4hCBUPd6vYZl8q4cn-IW3dNd-R/s1600/Content-Security-Policy-5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="123" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCZ2JM_ca81W97fTwq_7flnWasreXaUgq5vfdy4mNTD3Mg4SoVQguuS9u9-IOJNCmCKJSFmqc8R_d_glzdy_NVdM513sGEYCj7q1g5o20YULbWrSu9_t4hCBUPd6vYZl8q4cn-IW3dNd-R/s320/Content-Security-Policy-5.png" width="320" /></a></div>
<br /></div>
<strong>Firefox</strong> not only displays <abbr title="Content Security Policy">CSP</abbr> Error but also <abbr title="Content Security Policy">CSP</abbr> Warning:<br />
<br />
<pre><abbr title="Content Security Policy">CSP</abbr> ERROR: Couldn't parse invalid source 'unsafe-inline'
<abbr title="Content Security Policy">CSP</abbr> WARN: Failed to parse unrecoginzied source 'unsafe-inline'
</pre>
<pre></pre>
<strong>Firefox</strong> is also NOT appending '<tt>document-uri</tt>' directive in the report.<br />
<br />
<strong>Firefox</strong> absolute-links the '<tt>blocked-uri</tt>' however <strong>Google Chrome</strong> just appends the <tt>origin</tt> only.<br />
<br />
For "<strong>inline script base restriction</strong>"; Firefox uses '<tt>self</tt>' as '<tt>blocked-uri</tt>' value however Google Chrome ignores it.<br />
<br />
<span style="font-size: large;">For further reading:</span><br />
<ol>
<li><a href="http://www.w3.org/TR/CSP/">http://www.w3.org/TR/CSP/</a></li>
<li><a href="https://wiki.mozilla.org/Security/CSP/Specification">https://wiki.mozilla.org/Security/CSP/Specification</a></li>
<li><a href="http://www.html5rocks.com/en/tutorials/security/content-security-policy/">http://www.html5rocks.com/en/tutorials/security/content-security-policy/</a></li>
</ol>
<div>
<b>Updated at: Jan 15, 2012</b></div>
</div>
<div>
<span style="font-size: large;"><br /></span></div>
<div>
<span style="font-size: large;"><span style="color: red;">Unprefixed </span>support for <span style="color: red;">Content Security Policy </span><a href="http://blog.chromium.org/2013/01/content-security-policy-and-shadow-dom.html">in Chrome 25 Beta</a></span></div>
<div>
<a href="http://www.html5rocks.com/en/tutorials/security/content-security-policy/">Content Security Policy</a> (CSP) helps you reduce the risk of cross-site scripting and other content injection attacks. Starting in today’s Beta release, you can use the <a href="http://updates.html5rocks.com/2012/11/Content-Security-Policy-1-0-is-officially-awesome">unprefixed</a> Content-Security-Policy HTTP header to define a whitelist of trusted content sources. The browser will only execute or render resources from those sources. For example:</div>
<div>
<br />
<div class="separator" style="background-color: white; font-family: Arial, Helvetica, sans-serif; font-size: 12px; line-height: 15.598214149475098px; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi09-n5ny4DMryrlL-ZG_6YskAAF48T7cQTfP92ZK4f1KOHHpQ7GQjqKTPzGFAJqxoGfKZtRf2PGFZZrPVe5Vo1wQePidk-mr_kbYpJ1VhVQgQjt9qx7G3O39AZle5zefRZNzT0kXoqwck/s1600/csp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="42" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi09-n5ny4DMryrlL-ZG_6YskAAF48T7cQTfP92ZK4f1KOHHpQ7GQjqKTPzGFAJqxoGfKZtRf2PGFZZrPVe5Vo1wQePidk-mr_kbYpJ1VhVQgQjt9qx7G3O39AZle5zefRZNzT0kXoqwck/s400/csp.png" style="border: 1px solid rgb(204, 204, 204); padding: 4px;" width="400" /></a></div>
</div>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-20909701681580187992012-06-18T16:56:00.000+05:002017-04-28T22:31:30.610+05:00Using BackgroundWorker in Windows Forms Application<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: center;">
<span style="font-size: x-large;"><a href="http://webrtcweb.com/old/BackgroundWorker-Demo-in-Windows-Forms-App.zip.zip">Download Demo</a> - <a href="http://webrtcweb.com/old/BackgroundWorker-Project-in-Windows-Forms-App.zip.zip">Download Project</a></span></div>
I created a simple <b>Windows Forms</b> project using <b>C#</b> while taking advantage of <b>BackgroundWorker </b>object to run task at backward <b>thread</b>. <br />
<br />
You just have to change/alter only one method: "<b>CallMeBackward</b>" which is called at backward thread in the demo project.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmSaznb0pMeszTgU-5ASJUwLw_X3Jpt6FZ5mPMMO6elg1uk3EbyMHC0e_uCgeH8Np8QcGdjKKe_W6SKbR0nn5KfHGmjbV3538cZHXt21spkFFmoFUccJWkbc_lsbLcCvSTwiZ33Otx5J6x/s1600/BackgroundWorker-in-Windows-Forms-Application.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmSaznb0pMeszTgU-5ASJUwLw_X3Jpt6FZ5mPMMO6elg1uk3EbyMHC0e_uCgeH8Np8QcGdjKKe_W6SKbR0nn5KfHGmjbV3538cZHXt21spkFFmoFUccJWkbc_lsbLcCvSTwiZ33Otx5J6x/s640/BackgroundWorker-in-Windows-Forms-Application.png" width="640" /></a></div>
<br />
<br />
<pre>using System;
using System.ComponentModel;
using System.Threading;
namespace BackgroundWorkerInWindowsFormsApp
{
public partial class Form1 : System.Windows.Forms.Form
{
BackgroundWorker _backgroundWorker = new BackgroundWorker();
/*============================================================================*/
public Form1()
{
InitializeComponent();
/*============================================================================*/
_backgroundWorker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWorkEvent);
_backgroundWorker.WorkerSupportsCancellation = true;
ThreadPool.QueueUserWorkItem(_backgroundWorker.RunWorkerAsync);
/*============================================================================*/
}
/*============================================================================*/
void BackgroundWorkerDoWorkEvent(object sender, DoWorkEventArgs e)
{
BackgroundWorker backgroundWorker = sender as BackgroundWorker;
string argument = (string)e.Argument;
e.Result = OnTimeConsumingOperation(backgroundWorker, argument);
}
/*============================================================================*/
string OnTimeConsumingOperation(BackgroundWorker bw, string arg)
{
ThreadPool.QueueUserWorkItem(_ =>
{
CallMeBackward();
});
return string.Empty;
}
/*============================================================================*/
void <span id="CallMeBackward">CallMeBackward</span>()
{
for (var i = 0; i < 2000; i++)
{
counterLabel.Text = i.ToString();
timerLabel.Text = DateTime.Now.ToLongTimeString();
}
}
/*============================================================================*/
protected override void Dispose(bool disposing)
{
_backgroundWorker.CancelAsync();
_backgroundWorker.Dispose();
/*============================================================================*/
var timer = new System.Windows.Forms.Timer();
timer.Tick += (obj, evt) =>
{
if (!_backgroundWorker.IsBusy)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
};
timer.Start();
}
/*============================================================================*/
}
}
</pre>
<pre></pre>
<b>UI </b>elements are not thread-safe and should be accessed only on the thread that created them. Otherwise you'll get following error message:<br />
<br />
<div style="color: red;">
<i>Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.</i></div>
<div style="color: red;">
<br /></div>
<b>BackgroundWorker </b>cannot intercept main thread for UI elements. You've to use main thread instead.</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-6855095101426487262012-05-16T16:53:00.003+05:002017-04-28T22:32:14.935+05:00Released Canvas Designer<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: center;">
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;"><span style="font-size: x-large;"><br />
</span></span></div>
<div style="text-align: center;">
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;"><span style="font-size: x-large;">Canvas Designer</span></span></div>
<div style="text-align: center;">
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;"><span style="font-size: x-large;"><br />
</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvas-designer.appspot.com/"><img border="0" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi42OGPxqHT2QtYx236OtQz2M2GIVVGoAkywxVgWrHAjP81tCc7wwq9rk7xZ5djV33_IcCF6p3LfUadT-scjzaUYtf0Dnpk1XDIPkVP2AZfDL2oW5uhAJBYPxlSPVjsTQK-MvH76Y4yX5gQ/s640/Canvas-Designer.PNG" width="640" /></a></div>
<div style="text-align: center;">
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;"><span style="font-size: x-large;"><br />
</span></span></div>
<br />
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;"><i><a href="https://canvas-designer.appspot.com/">A tool</a> aimed to give you a full-fledged drawing surface and also auto generate appropriate code for you in different formats!</i></span><br />
<br />
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;">It targets Canvas 2D Context – i.e. it gives you a built-in IDE for Canvas 2D APIs!</span><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Try the tool:</b><br />
<a class="ot-anchor" href="https://canvas-designer.appspot.com/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; line-height: 18px; text-decoration: none;">https://canvas-designer.appspot.com/</a><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Introduction video:</b><br />
<a class="ot-anchor" href="http://www.youtube.com/watch?v=oSSwMlBu8SY" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; line-height: 18px; text-decoration: none;">Introducing Canvas Designer</a><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Want to contribute or go in-depth:</b><br />
<a class="ot-anchor" href="http://muaz-khan.github.com/Everything/Canvas/Tools/Designer/Help/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; line-height: 18px; text-decoration: none;">http://muaz-khan.github.com/Everything/Canvas/Tools/Designer/Help/</a><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Supported features:</b><br />
<span style="background-color: white; font-family: "arial" , sans-serif; line-height: 18px;">Bezier and quadratic curves, arc (circles!), lines, rectangles, fill/stroke styles, line width/cap/join, global alpha and other properties.</span><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Some funny simple sketches:</b><br />
<a class="ot-anchor" href="http://muaz-khan.github.com/Everything/Canvas/Sketches/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; line-height: 18px; text-decoration: none;">http://muaz-khan.github.com/Everything/Canvas/Sketches/</a><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; line-height: 18px;">Flying Bird [work-in-progress!] (Experiment):</b><br />
<a class="ot-anchor" href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Flying-Bird/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; line-height: 18px; text-decoration: none;">http://muaz-khan.github.com/Everything/Canvas/Experiments/Flying-Bird/</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-46625712715173128322012-05-15T18:17:00.001+05:002017-04-28T22:32:37.852+05:00How I use CSS?<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "verdana" , sans-serif;">In this article, I am going to explorer how I use CSS – i.e. my way of using CSS – or how I design using CSS?</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I prefer </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">classes</span></span><span style="font-family: "verdana" , sans-serif;"> over </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">tags</span></span><span style="font-family: "verdana" , sans-serif;"> and </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">IDs</span></span><span style="font-family: "verdana" , sans-serif;">.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.class-name { }
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">It doesn't mean that I don't use <tags> and #ids – I use them when appropriate!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">My CSS classes are lowercase, hyphen-separated:</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.header-top
.header-h1
.red-background
.skew-90deg
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I always place </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">repeated code</span></span><span style="font-family: "verdana" , sans-serif;"> in a </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">separate class</span></span><span style="font-family: "verdana" , sans-serif;">, and use it on demand. In future, I don't have to change CSS anymore; I just have to edit HTML class attributes:</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.</span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">red-background</span></span><span style="font-family: "verdana" , sans-serif;"> { }
<span class="</span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">red-background</span></span><span style="font-family: "verdana" , sans-serif;">">go ahead</span>
<div class="allow-select </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">red-background</span></span><span style="font-family: "verdana" , sans-serif;">">Long DIV selectable</div>
<a class="skew-90 rotate-180 </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">red-background</span></span><span style="font-family: "verdana" , sans-serif;">">Login</a>
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-style: italic;"><span style="font-family: "verdana" , sans-serif;">Above code is much better than using comma separated selectors in CSS, in my opinion!</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I never use </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> in my code, trust me, never!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I don't use </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> because </span><span style="font-style: italic;"><span style="font-family: "verdana" , sans-serif;">it overrides previous styles</span></span><span style="font-family: "verdana" , sans-serif;"> and whenever I've to override any previous style; I place overridden code at the bottom of the CSS file without using </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> - so everything works fine for me!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> NEVER allow upcoming code to override it until you explicitly use </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> in the later code! That's why I never use it.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">For the sake of clarity and simplicity, I always order CSS selectors according to HTML elements priority.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">(ORDER OF CSS SELECTORS)</span></span><span style="font-family: "verdana" , sans-serif;">
html, body top most priority
header, h1 second priority
menu third priority
.content fourth priority
footer last priority (+media queries etc.)
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">This makes me comfortable regarding my CSS and I feel no confusion in later edits.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I use child, attribute and sibling selectors often without concerning IE6! - </span><span style="font-style: italic;"><span style="font-family: "verdana" , sans-serif;">It is progressively enhanced way of using CSS!</span></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">For example see this code:</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">footer ~ * {
display: none !important;
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">If someone dynamically appends HTML code at my page! I'll hide his HTML; however if he uses </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;"> himself; there is no solution to hide his HTML!! – Remember this is the only place where I use </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">!important</span></span><span style="font-family: "verdana" , sans-serif;">. (Note: <a href="http://muaz-khan.blogspot.com/2012/06/exploring-csp-content-security-policy.html">I use CSP [Content Security Policy] for inline/out-line CSS/JS protection; however it is still in draft!</a> )</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I never like default styles; so I always override them.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I use </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">:after</span></span><span style="font-family: "verdana" , sans-serif;"> and </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">:before</span></span><span style="font-family: "verdana" , sans-serif;"> pseudo selectors in the way that my layout also looks great in old browsers like IE6.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">li:before {
content: "-";
margin-right: 5px;
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">IE6 will ignore it, and margins will NEVER apply.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I use negative margins too - IE6 is still stupid for this!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.dialog-box {
margin-top: -20px;
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">There are a lot other alternatives for negative margins (float, text/word wrapping etc.); but I don't know why I always use negative margins! – It is easy to use and gives control over stuff! (</span><b><span style="font-family: "verdana" , sans-serif;">Note:</span></b><span style="font-family: "verdana" , sans-serif;"> </span><span style="font-style: italic;"><span style="color: green;"><span style="font-family: "verdana" , sans-serif;">display: inline-block</span></span><span style="font-family: "verdana" , sans-serif;"> is not an alternative for negative margins!</span></span><span style="font-family: "verdana" , sans-serif;">) [Well, clearfixing is also helpful for old IE]</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">For anchors (</span><code><span style="font-family: "verdana" , sans-serif;"><a></span></code><span style="font-family: "verdana" , sans-serif;">), I use </span><code><span style="font-family: "verdana" , sans-serif;">border-bottom</span></code><span style="font-family: "verdana" , sans-serif;"> instead of </span><code><span style="font-family: "verdana" , sans-serif;">text-decoration</span></code><span style="font-family: "verdana" , sans-serif;"> – it looks great!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">a {
text-decoration: none;
border-bottom: 1px solid blue;
padding-bottom: 3px;
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">You may know that CSS values' order doesn't matter; however I always follow standard (common) style. As you can see the value of </span><code><span style="font-family: "verdana" , sans-serif;">border-bottom</span></code><span style="font-family: "verdana" , sans-serif;">; this is most common style on the web!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Negative </span><code><span style="font-family: "verdana" , sans-serif;">text-indent</span></code><span style="font-family: "verdana" , sans-serif;"> is better for hiding text over images – I use it when needed:</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.menu-button {
background-image: url(about.png);
text-indent: -111111px;
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I use </span><code><span style="font-family: "verdana" , sans-serif;">position:absolute</span></code><span style="font-family: "verdana" , sans-serif;"> often! - sometimes I also use </span><code><span style="font-family: "verdana" , sans-serif;">position:fixed</span></code><span style="font-family: "verdana" , sans-serif;"> - however I strongly care IE6 in that case!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">.fix-position {
position: fixed;
*position: absolute; /* IE6 Hack */
}
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I never used </span><code><span style="font-family: "verdana" , sans-serif;">visibility:hidden</span></code><span style="font-family: "verdana" , sans-serif;"> - I use </span><code><span style="font-family: "verdana" , sans-serif;">display:none</span></code><span style="font-family: "verdana" , sans-serif;"> instead. I know </span><code><span style="font-family: "verdana" , sans-serif;">display: none</span></code><span style="font-family: "verdana" , sans-serif;"> causes reflow but I still use it!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I use </span><code><span style="font-family: "verdana" , sans-serif;">line-height</span></code><span style="font-family: "verdana" , sans-serif;"> in </span><span style="color: #781314;"><span style="font-family: "verdana" , sans-serif;">unit-less</span></span><span style="font-family: "verdana" , sans-serif;"> format:</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">p, p {
line-height: 1.5;
}
</span></pre>
<div style="font-style: italic;">
<span style="font-family: "verdana" , sans-serif;">It inherits value from </span><code><span style="font-family: "verdana" , sans-serif;">font-size</span></code><span style="font-family: "verdana" , sans-serif;">.</span></div>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">In case when CSS value is zero; I use zero units-less!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">When editing CSS within JavaScript; I use </span><code><span style="font-family: "verdana" , sans-serif;">display:''</span></code><span style="font-family: "verdana" , sans-serif;"> for allowing elements get their default display value.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<pre><span style="font-family: "verdana" , sans-serif;">header.style.display = '';
</span></pre>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I love well-decorated code and I love using Unicode special characters to decorate and clarify my code!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">There are many other tricks too – This is how I use CSS – Are you have any question, suggestion etc.?</span></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-75764637740149490132012-02-14T15:00:00.001+05:002017-04-28T22:33:10.575+05:00Absolute or Relative URL issues and the solution<div dir="ltr" style="text-align: left;" trbidi="on">
<link href="https://sites.google.com/site/muazkh/coding.styles.css" rel="stylesheet"></link> <br />
This article demonstrates the issues occur due to the usage of relative URLs while shifting pages from one location to another or the misuse of relative URLs and how to overcome those issues.<br />
<br />
<span style="font-size: large;">Relative URLs examples:</span><br />
<pre><span class="comment">/* HTML example */</span>
<img src="<span class="string">image.jpg</span>" />
<span class="comment">/* CSS example */</span>
.image-div
{
<span class="keyword">background-image: url</span>(image.jpg);
}
<span class="comment">/* JavaScript example */</span>
image.src = 'image.jpg';
</pre>
<br />
<span style="font-size: large;">Relative URLs and expected issues</span><br />
In simple words, relative URL reference link on the same server (or domain).<br />
<ul>
<li><strong><img src="image.jpg" /></strong> Reference of a resource from same directory<br />
<br />
</li>
<li><strong><img src="../../image.jpg" /></strong> In CSS/HTML, <strong>../</strong> used to reference parent directory. In the above example, <strong>../</strong> is used two times; so it is referencing a resource from 2-level parent directory.<br />
<br />
</li>
<li class="last"><strong><img src="sub-directory/image.jpg" /></strong><br />
Reference of a resource from sub directory<br />
</li>
</ul>
The issue occurs when when shifting a page from one location to another which cause broken links.<br />
<br />
<span style="font-size: large;">/ and ../ are two different things:</span><br />
<ul>
<li><strong><img src="/image.jpg" /></strong><br />
Reference of a resource from root directory</li>
<li> <strong><img src="../image.jpg" /></strong><br />
Reference of a resource from 1-level parent directory<br />
<br />
</li>
<li class="last"><strong><img src="image.jpg" /></strong><br />
Reference of a resource from same directory</li>
</ul>
<span style="font-size: large;">Absolute URLs: The solution or ...</span><br />
Absolute URLs solves most of relative URLs issues and also helps prioritized your website (SEO!). Absolute URLs also help testers or late-viewers to understand (+access) the located resource easily.<br />
<br />
In JavaScript, we can create a global variable to store domain (root) URL:<br />
<br />
<pre><span class="comment">/* Global variable */</span>
var domainName = location.protocol + '//' + location.host;
<span class="comment">/* Usage */</span>
image.src = domainName + <span class="string">'/image.jpg'</span>;
</pre>
<br />
<a href="http://james.padolsey.com/" rel="nofollow" target="_blank">JAMES PADOLSEY</a> way of <a href="http://james.padolsey.com/javascript/getting-a-fully-qualified-url" rel="nofollow" target="_blank">getting a fully qualified (absolute) URL</a> in JavaScript:<br />
<br />
<pre><span class="keyword">function</span> qualifyURL(url) {
<span class="keyword">var</span> img = document.createElement('<span class="string">img</span>');
img.src = url; <span class="comment">// set string url</span>
url = img.src; <span class="comment">// get qualified/absolute url</span>
img.src = <span class="keyword">null</span>; <span class="comment">// no server request</span>
<span class="keyword">return</span> url;
}
<span class="comment">// Tested successfully in IE6/7, FF2/3, Safari3, Opera9, Chrome (All on Windows XP)</span>
<span class="comment">// EXAMPLE:</span>
<span class="keyword">var</span> myRelativeURL = '<span class="string">blah/page.html</span>';
alert( qualifyURL(myRelativeURL) );
</pre>
<br />
Absolute URLs are helpful when shifting a page from one location to another because you don't have to change resource's URLs from within the shifted page.<br />
<br />
A good link for parsing or making absolute URLs in JavaScript: <a href="https://gist.github.com/1088850" rel="nofollow" target="_blank">https://gist.github.com/1088850</a><br />
<br />
<span style="font-size: large;">Protocol Level URLS:</span><br />
<pre><script src="//www.google.com/js/gweb/analytics/autotrack.js"></script>
<img src="//domain/image.jpg" />
</pre>
<a href="http://google-styleguide.googlecode.com/svn/trunk/htmlcssguide.xml?showone=Protocol#Protocol">Omitting the protocol—which makes the URL relative—prevents mixed content issues and results in minor file size savings.</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-20530652012454664282012-02-12T16:42:00.003+05:002017-04-28T22:33:37.647+05:00What is Curvature?<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-size: x-large;"><a href="https://canvature.appspot.com/">Curvature tool - Test Online</a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvature.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHOPsREIUOaRPotU0vB_29JHgzqVLNIfx4sQc6E-s0yi_kQJXIfvC_rP89mD41VRwp-pDpP9kJ_1tHRBLyFXFkSeu1N9lbzULKEn1aiagFIuVUaojDFK7LJCb3U_JNfvCY7xXJfj4sf86V/s320/Curvature-HTML5-Curves-Generator-Tool-2.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvature.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJJZBc1uMMalxKMXi4J1jn2J30SsSL8B7B0TvPO9VngX9xDkmzWg-CqNmWbkFyODGkMvcg11FgIy8mBylW1vLOsegnXz24krBRyO3Er3-3dZvI1TUKGlWYmmST2pat2yOltrWWeWxVvjms/s320/Curvature-HTML5-Curves-Generator-Tool-3.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvature.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNbZ1jafnEcoAS0nJ_QZ1i9db8OJGFxqvrGd0_kA2ixQ3GvkSIkvM8UXpAAGkpNVxPlZodc9IO04Ji_lRy1ZSp-5iBQgOLqj4CTpI88deDEtHjZv-3AAZX4aVU0XwAnSlhlvhOi9X0uNNb/s320/Curvature-HTML5-Curves-Generator-Tool.png" width="320" /></a></div>
<br />
<br />
<span style="font-size: large;">What is the <a href="https://canvature.appspot.com/">Curvature</a>?</span><br />
<a href="https://canvature.appspot.com/">Curvature</a> is a designer for curving. It generates Canvas 2d APIs relevant code in different forms.<br />
<br />
<span style="font-size: large;">Top Features</span><br />
<a href="https://canvature.appspot.com/">Curvature </a>allows you:<br />
<ul>
<li>Curves alignment (in any shape, in any portion of the page, to any point!) </li>
<li>Drag whole drawing (all curves at a time!) </li>
<li>Drag a single curve (all points at a time) </li>
<li>Copy whole drawing and dragging-ability! Generates code in 4 forms: 1) relative coordinates 2) shortened relative coordinates 3) absolute coordinates 4) shortened absolute coordinates</li>
</ul>
<span style="font-size: large;">Other common features</span><br />
<ul>
<li>Undo support (for unlimited curves; whether millions of curves!) </li>
<li>Starting and ending points replacement for a Bezier curve (sometimes: useful) </li>
<li>Auto creates new curves (on mouse movement – on demand!) </li>
<li>Fill color (in the last curve – clarifies the curve!)</li>
</ul>
<br />
New version of Curvature allows you hide/show the coding area so you can have a full drawing surface now!<br />
<br />
<span style="font-size: large;">Source code (Github!)</span><br />
Source code of <a href="http://muaz-khan.github.com/Curvature/">Curvature</a> available at Github! <a href="https://github.com/muaz-khan/Curvature">https://github.com/muaz-khan/Curvature</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://canvature.appspot.com/" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6jBl36doyVn3PzybhNT57AqFXNxSy5nt5cMoK4he3UTa0N3bfaTY6wt8_dUtYEStc_sY82kbNg8oxjfnEdzfQk5Bt6FrJCAni6VdPVPJKqj4ZSYNNOZJIWjQwWLekUAy76DWquRmk4z0p/s320/Curvature-features.png" width="320" /></a></div>
<br />
<br />
<span style="font-size: large;">What is "Shorten the code"?</span><br />
This feature shortens the lines of auto generated code! Also it reduces size of the page!<br />
<br />
<span style="font-size: large;">Explain "Make relevant startingX/startingY points"</span><br />
This feature was included in the first release of the Curvature. It generates relevant code from two points: startingX and startingY.<br />
<br />
This feature allows you use e.pageX/e.pageY on these two points so you can easily drag/move whole shape (all curves at a time) without any disturbance.<br />
<br />
<span style="font-size: large;">Is it useful: "Replace starting and ending points' positions at new curve"?</span><br />
Sure, in the complex shapes – I used it often.<br />
<br />
<span style="font-size: large;">"Auto create new curves" – what is this?</span><br />
To auto create new curves at mouse movement. You don't have to click "New Curve" button or press "N" key to create new curve(s).<br />
<br />
<span style="font-size: large;">"Temporarily fill a color in the last curve" – isn't it a redundant feature?</span><br />
For redundancy: it can be a redundant feature. However I included it for those who want to clarify the last curve!<br />
<br />
<span style="font-size: large;">"Drag whole curve/drawing" – Explain</span><br />
<ul>
<li>"<b>Drag whole curve</b>" feature drags last curve as it is without any disturbance in the real shape of the "last" curve. </li>
<li>"<b>Drag whole drawing</b>" drags whole drawing (at x-y coordinates) without any disturbance in the real shape(s).</li>
</ul>
Both are most useful features in the <a href="https://canvature.appspot.com/">Curvature</a>!<br />
<br />
<span style="font-size: large;">"Copy current drawing (all curves)" – isn't it limited?</span><br />
This feature copies whole drawing and allows you drag "copied drawing". It is limited because it allows only one "paste" for copied drawing. Upcoming version of Curvature may support it fully – till that; use it carefully!<br />
<br />
<span style="font-size: large;">Curves alignment – digging into it</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-RxEWeiZB7FMMh5f2qTYftfTuEZ2FDqLv8Vai_oN3h7fNA_DSwEtrmyD9xdtmgt_OlWx-TaWjTWrR6q-D0tQd8dIb3dZITULBSMPhMAVJ7KkwCfP1xzNHaNS4z8qrvwn3CuEVQDio467N/s1600/Curvature-Curves-Alignment.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-RxEWeiZB7FMMh5f2qTYftfTuEZ2FDqLv8Vai_oN3h7fNA_DSwEtrmyD9xdtmgt_OlWx-TaWjTWrR6q-D0tQd8dIb3dZITULBSMPhMAVJ7KkwCfP1xzNHaNS4z8qrvwn3CuEVQDio467N/s320/Curvature-Curves-Alignment.png" width="320" /></a></div>
<br />
<br />
<a href="https://canvature.appspot.com/">Curvature</a> allows you align curves in two styles:<br />
<ul>
<li><b>"Align Vertically":-</b> Aligns y-coordinates of all (or selected) points. </li>
<li><b>"Align Horizontally" (default selected):-</b> Aligns x-coordinates of all (or selected) points.</li>
</ul>
<br />
"<b>Align to relevant topmost points</b>" (default selected) – aligns points with topmost (or first drawn) curve's relevant points.<br />
<br />
You can set custom points too for alignment.<br />
<br />
<b>Test the tool:</b> <a href="https://canvature.appspot.com/">https://canvature.appspot.com/</a></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-68863900280790879512012-02-12T15:24:00.001+05:002017-04-28T22:34:04.124+05:00Dragging/Moving shapes smoothly using Canvas 2d APIs<div dir="ltr" style="text-align: left;" trbidi="on">
<link href="https://sites.google.com/site/muazkh/coding.styles.css" rel="stylesheet"></link> <br />
Did you ever write code to drag or move shapes using HTML5 Canvas 2d APIs? If you did, then didn't you ever face a situation (an unexpected behaviour) when you drag or move a shape (e.g. circle, rectangle, image, Bezier cubic/quadratic curve, etc.)?<br />
<br />
Fore a live example, please test following demo. Drag any shape (from its any corner):<br />
<b><br /></b>
<div style="text-align: center;">
<b><a href="http://muaz-khan.github.com/Everything/Canvas/Experiments/Dragging-or-Moving-Shapes-smoothly-using-HTML5-Canvas-2D-APIs/"><span style="font-size: large;">TEST ME! - THE DEMO</span></a></b></div>
<br />
Didn't you face a sudden jerk on the shape?<br />
<br />
Test bottom-part of the demo too. It is working smoothly. There is no sudden jerk!<br />
<br />
Let us dig into both examples<br />
<br />
<pre>circleX = e.pageX - canvas.offsetLeft;
circleY = e.pageY - canvas.offsetTop;
context.arc(circleX, circleY, 50, 0, Math.PI * 2, false);
</pre>
<br />
Each workaround regarding the shape's width/height or radius is inefficient or non-smooth:<br />
<ul>
<li>Extraction of the radius (one or two times) to accurately position the circle</li>
<li>Subtraction of half width/height of the shape (rectangle/image) to position it accordingly</li>
</ul>
<span style="font-size: large;">Second example/demo is the solution!</span><br />
<br />
But for second example, I've not used pageX/pageY directly; instead I'm doing following steps:<br />
<br />
<ul>
<li>Tracking first/previous mouse-down or touch position (x-y coordinates)</li>
<li>Extracting the dragged distance from those coordinates</li>
<li>Adding or subtracting the distance from shape's coordinates according to the dragging direction (positive or negative x-y axis)</li>
</ul>
<br />
<br />
<pre>/* Global Variable */
var previousPoints = [0, 0];
/* Mouse Down event! */
{
var x = ( e.pageX - canvas.offsetLeft ), y = ( e.pageY - canvas.offsetTop );
/* Saving last x-y coordinates! - on mousedown */
previousPoints = [x, y];
}
/* Mouse Move event! */
{
var x = e.pageX - canvas.offsetLeft,
y = e.pageY - canvas.offsetTop,
/* Previous points: x-y */
prevX = previousPoints[0],
prevY = previousPoints[1];
/* Drag direction! */
positiveX = x > prevX,
positiveY = y > prevY,
/* The dragged distance! */
value;
/* This value will be used for x-coordinates */
value = positiveX ? (x - prevX) : (prevX - x);
if (positiveX)
circleX += value;
else
circleX -= value;
/* This value will be used for y-coordinates */
value = positiveY ? (y - prevY) : (prevY - y);
if (positiveY)
circleY += value;
else
circleY -= value;
/* Saving last x-y coordinates! - on mousemove */
previousPoints = [x, y];
}
/* And to draw the circle! */
context.arc(circleX, circleY, 50, 0, Math.PI * 2, false);
</pre>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-64949182259307944922012-02-12T09:57:00.003+05:002017-04-28T22:34:26.024+05:00Quick updates in the Curvature<div dir="ltr" style="text-align: left;" trbidi="on">
<a class="ot-anchor" href="https://canvature.appspot.com/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">https://canvature.appspot.com/</a><br />
<br style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;" />
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;">Summary:</b><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> “Now you can directly share code with others!”</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※</span><br />
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;">Test what I shared with you!</b><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">① </span><a class="ot-anchor" href="http://goo.gl/5MdKo" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">http://goo.gl/5MdKo</a><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> </span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">② </span><a class="ot-anchor" href="http://goo.gl/tZAuJ" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">http://goo.gl/tZAuJ</a><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> </span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">③ </span><a class="ot-anchor" href="http://goo.gl/XXVjg" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">http://goo.gl/XXVjg</a><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">④ </span><a class="ot-anchor" href="http://goo.gl/Y8NqT" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">http://goo.gl/Y8NqT</a><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">This feature is not only helpful for sharing but also for saving your last drawing or settings via bookmarking.</span><br />
<br style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;" />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">Click → </span><b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;">“Share”</b><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> button – and share URL! (Hint: use </span><a class="ot-anchor" href="http://goo.gl/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-decoration: none;">http://goo.gl/</a><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> to shorten the URL!!)</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">★ </span><b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;">Note</b><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> ⇒ </span><i style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px;">Sometimes URL goes too long. In that case, open browser specific dev-tool (<b>F12</b> or whatever shortcut!) and type <b>location.hash</b> in the <b>Console</b> window/section. (And copy!)</i></div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-54990373146162015962012-02-08T14:19:00.002+05:002017-04-28T22:35:10.465+05:00Curvature is a designer allows you draw curves<div dir="ltr" style="text-align: left;" trbidi="on">
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-align: -webkit-auto;">Releasing <a class="ot-hashtag" href="https://canvature.appspot.com/" style="color: #3366cc; cursor: pointer; text-decoration: none;">Curvature</a> second time!</b><br />
<br />
<a href="https://canvature.appspot.com/">https://canvature.appspot.com/</a>
<br />
<br />
<a class="ot-hashtag" href="https://canvature.appspot.com/" style="background-color: white; color: #3366cc; cursor: pointer; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-align: -webkit-auto; text-decoration: none;">Curvature</a><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> is a designer allows you draw curves. It generates Canvas 2d APIs relevant code in different forms/styles.</span><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-align: -webkit-auto;">Today release contains following new features:</b><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">➛ Curves Alignment (align any point, to any coordinate of the screen)</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">➛ You can drag whole curve (as it is!)</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">➛ You can drag whole drawing (all curves – without any disturbance!)</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">➛ You can copy whole drawing exactly!</span><br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">➛ You can get shortened code too!</span><br />
<br />
<span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;">Today version also allows you hide/show the coding area; so you can have a full drawing surface, now!</span><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-align: -webkit-auto;">Curvature:</b><span style="background-color: white; font-family: "arial" , sans-serif; font-size: 13px; line-height: 18px;"> </span><span style="font-family: "arial" , sans-serif; font-size: x-small;"><span style="line-height: 18px;"><a href="https://canvature.appspot.com/">https://canvature.appspot.com/</a></span></span><br />
<br />
<b style="background-color: white; font-family: arial, sans-serif; font-size: 13px; line-height: 18px; text-align: -webkit-auto;">To know the Curvature and too see some screen shots:</b><br />
<a href="http://muaz-khan.blogspot.com/2012/02/what-is-curvature.html">http://muaz-khan.blogspot.com/2012/02/what-is-curvature.html</a>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-45470695899992586932012-02-04T17:00:00.002+05:002012-02-14T16:51:56.932+05:00Dragging/Moving shapes smoothly using Canvas 2d APIs<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="background-color: #fcfcfc; font-family: Verdana, sans-serif;">Did you ever write code to drag or move shapes using </span><b style="background-color: #fcfcfc; font-family: Verdana, sans-serif;">Canvas 2d APIs</b><span style="background-color: #fcfcfc; font-family: Verdana, sans-serif;">? If you did, then didn't you ever face a situation (an unexpected behaviour) when you drag or move a shape (e.g. circle, rectangle, image, </span><b style="background-color: #fcfcfc; font-family: Verdana, sans-serif;">Bezier </b><span style="background-color: #fcfcfc; font-family: Verdana, sans-serif;">cubic/quadratic curve, etc.)?</span><br />
<div style="background-attachment: initial; background-clip: initial; background-color: #fcfcfc; background-image: initial; background-origin: initial; margin-bottom: 0.0001pt; margin-left: 0in; margin-right: 0in; margin-top: 10pt; text-align: -webkit-auto; word-wrap: break-word;">
<span style="font-family: Verdana, sans-serif;">This post helps you write code to drag or move shapes as smoothly and accurately as possible using <b>HTML5 Canvas 2d API</b>s.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span><br />
<a href="http://muaz-khan.blogspot.com/2012/02/draggingmoving-shapes-smoothly-using_12.html">http://muaz-khan.blogspot.com/2012/02/draggingmoving-shapes-smoothly-using_12.html</a>
</div>
</div>Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-41127116484143774212012-02-04T16:50:00.000+05:002012-06-09T14:36:05.093+05:00Curvature: HTML5 Canvas Curves Generator/Tool<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br class="Apple-interchange-newline" />I designed <b><a href="http://muaz-khan.github.com/Curvature/">Curvature</a></b>: a tool gives you full control over <b>Bezier </b>cubic/quadratic <b>curves</b>. <i>This tool is under production</i>.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br />
</span><br />
<a href="http://muaz-khan.github.com/Curvature/">http://muaz-khan.github.com/Curvature/</a> <span style="font-family: Verdana, sans-serif;"><br /></span><br />
<br />
<a href="http://muaz-khan.blogspot.com/2012/02/what-is-curvature.html">What is Curvature?</a><br />
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">I’ll soon release<a href="http://muaz-khan.github.com/Curvature/"> updated version of this tool</a>. Till that use this old version.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br />
</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><b>Features of this (old) version:</b></span></div>
<div class="MsoNormal">
</div>
<ul>
<li><span style="font-family: Verdana, sans-serif;">Lets you draw unlimited curves</span></li>
<li><span style="font-family: Verdana, sans-serif;">Undo facility for unlimited curves</span></li>
<li><span style="font-family: Verdana, sans-serif;">Generates code for you in relative/absolute coordinates</span></li>
</ul>
<br />
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br />
</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><b><a href="http://muaz-khan.github.com/Curvature/">New version that I’ll release soon</a> contains following features:</b></span></div>
<div class="MsoNormal">
</div>
<ul>
<li><span style="font-family: Verdana, sans-serif;">Drag whole curve smoothly</span></li>
<li><span style="font-family: Verdana, sans-serif;">Drag whole drawing (all curves at a time) smoothly</span></li>
<li><span style="font-family: Verdana, sans-serif;">Relative coordinates for each point (advanced!)</span></li>
<li><span style="font-family: Verdana, sans-serif;">Alignment of the curves (for each point!)</span></li>
</ul>
<br />
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br />
</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">And a lot more!</span></div>
<br />
<span style="font-family: Verdana, sans-serif;"><a href="https://github.com/muaz-khan/Curvature">https://github.com/muaz-khan/Curvature</a></span></div>Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.comtag:blogger.com,1999:blog-5417785852849425449.post-60029605107566889412011-09-27T06:54:00.002+05:002017-04-28T22:36:02.879+05:00C# and wiki markup parsing<div dir="ltr" style="text-align: left;" trbidi="on">
Wiki Markup is the syntax or keyword used by wiki websites to format a webpage. You can use *<b>asterisks</b>* around a word for <b>bold</b> and _<i>underscore</i>_ for <i>italic</i> and this is common and easy to adopt.<br />
<br />
<b>Examples:</b><br />
<br />
<ul>
<li>_<i>italic</i>_ or {{<i>italic</i>}}</li>
<li>*<b>bold</b>* or ((<b>bold</b>))</li>
</ul>
<br />
<br />
Wikipedia is using a <a href="http://en.wikipedia.org/wiki/Help:Wiki_markup">different style of wiki markup</a> and Google project hosting (as well as Google+) is using the <a href="http://code.google.com/p/support/wiki/WikiSyntax">same wiki markup</a> I demonstrated above.<br />
<br />
<span style="font-size: large;">Implementation in C#</span><br />
I am using <b>Regex</b> to implement wiki markup in <b>C#</b>.<br />
<br />
And here is the method that does the entire job required for parsing any kind of wiki markup.<br />
<br />
<pre>private static string ReplaceWikiMarkup(this string inputString, IList<string> pairs,
IList<string> startTag, IList<string> endTag)
{
for (var i = 0; i < pairs.Count; i++)
{
var r = new Regex(pairs[i] + "(.*?)" + pairs[i]);
var matches = r.Matches(inputString);
for (var j = 0; j < matches.Count; j++)
{
inputString = inputString.Replace(matches[j].Groups[0].Value,
startTag[i] +
matches[j].Groups[1].Value.Trim() +
endTag[i]);
}
}
return inputString;
}
</pre>
<pre></pre>
Using (calling) above method:
<br />
<br />
<pre>public static string ReplaceWikiMarkup(this string inputString)
{
inputString = inputString.Replace("*", "-b-"); <span class="comment">/* becasue asterisk(*) is a reserved word in Regex */</span>
return inputString.ReplaceWikiMarkup(
new[] { "-b-", "_" },
new[] { "<b>", "<i>" }, <span class="comment">/* SEO: use <strong> instead */</span>
new[] { "</b>", "</i>" });
}
</pre>
<pre></pre>
<span style="font-size: large;">Understanding the method </span><br />
There are 4 arguments we are passing to the method:
<br />
<ul>
<li><strong>inputString</strong> - Input in any format </li>
<li><strong>pairs</strong> - Wiki Markup syntax or keywords: *<strong>Asterisk</strong>*, _<i>underscore</i>_ or anything else you use</li>
<li><strong>startTag</strong> - This can be HTML element’s empty starting tag or stating tag with any number of attributes
<strong>Examples:</strong> <b><i><strong><h2 class="something"><pre style="something: something ;"><br />
</li>
<li><strong>endTag</strong> - This is the ending tag of HTML element
<strong>Examples:</strong> </b></i></strong></h2></pre><br />
</li>
</ul>
You must be careful for the ordering of HTML elements for <strong>startTag</strong>
and <strong>endTag</strong> parameters. I mean that if <strong>startTag</strong>’s
first element is <b> then <strong>endTag</strong>’s first element must be
</b> and so on.
Heading: Extending the method
Let us assume that you want to use {{{ }}} or <code></code> as wiki markup for letting end-user inject the code.
<br />
<ul>
<li><strong>Code:</strong> {{{ --- }}} or <code> --- </code></li>
<li><strong>Heading:</strong> [[ Heading ]]</li>
<li class="last"><strong>Bold:</strong> (( Bold ))</li>
</ul>
<pre>public static string ReplaceWikiMarkup(this string inputString,
IList<string> startKeywords, <span class="comment">/* {{{ or <code> or <h> */</span>
IList<string> endKeywords, <span class="comment">/* }}} or </code> or </h> */</span>
IList<string> startTag, <span class="comment">/* HTML equivalent starting tag */</span>
IList<string> endTag) <span class="comment">/* HTML equivalent ending tag */</span>
{
for (var i = 0; i < startKeywords.Count; i++)
{
var r = new Regex(startKeywords[i] + "(.*?)" + endKeywords[i]);
var matches = r.Matches(inputString);
for (var j = 0; j < matches.Count; j++)
{
inputString = inputString.Replace(matches[j].Groups[0].Value,
startTag[i] + matches[j].Groups[1].Value.Trim() +
endTag[i]);
}
}
return inputString;
}
</pre>
<pre></pre>
And now we are ready to call above (changed) method:
<br />
<br />
<pre>public static string ReplaceWikiMarkup(this string inputString)
{
inputString = inputString.Replace("*", "-b-");
return inputString.Replace(
new[] { "-b-", "_", "<h>", "<code>" },
new[] { "-b-", "_", "</h>", "</code>" },
new[] { "<b>", "<i>", "<h2>", "<pre>" },
new[] { "</b>", "</i>", "</h2>", "</pre>" });
}
</pre>
</div>
Muaz Khanhttp://www.blogger.com/profile/07782359057133550789noreply@blogger.com