Chrome | Firefox | Safari 11 | Edge | |
---|---|---|---|---|
Windows | + | + | + | |
Mac OS | + | + | + | |
Android | + | + | ||
iOS | - | - | + |
1. The browser connects to the server via the Websocket protocol and sends the publish command.
2. The browser captures the microphone and the camera and sends a WebRTC stream to the server.
3. The second browser establishes a connection also via Websocket and sends the play command.
4.The second browser receives the WebRTC stream and plays that stream on the page.
1. For this test we use the demo server at demo.flashphoner.com and the Two Way Streaming web application:
2. Establish a connection with the server by clicking the Connect button
3. Click Publish. The browser captures the camera and sends the stream to the server.
4. Make sure the stream us sent to the server and the system operates normally by opening chrome://webrtc-internals
5. Open the Two Way Streaming app in a new window, click Connect and specify the stream ID, then click Play.
6. Playback diagrams in chrome://webrtc-internals
Below is the call flow based on the Two Way Streaming example
1. Establishing connection to the server.
Flashphoner.createSession(); code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { setStatus("#connectStatus", session.status()); onConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED); onDisconnected(); }).on(SESSION_STATUS.FAILED, function () { setStatus("#connectStatus", SESSION_STATUS.FAILED); onDisconnected(); }); |
2. Receiving from the server the successful connection status.
ConnectionStatusEvent ESTABLISHED code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { setStatus("#connectStatus", session.status()); onConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED); onDisconnected(); }).on(SESSION_STATUS.FAILED, function () { setStatus("#connectStatus", SESSION_STATUS.FAILED); onDisconnected(); }); |
3. Publishing the stream.
stream.publish(); code
session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.PUBLISHING, function (stream) { setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); onPublishing(stream); }).on(STREAM_STATUS.UNPUBLISHED, function () { setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED); onUnpublished(); }).on(STREAM_STATUS.FAILED, function () { setStatus("#publishStatus", STREAM_STATUS.FAILED); onUnpublished(); }).publish(); |
4. Receiving from the server the successful publishing status.
StreamStatusEvent, статус PUBLISHING code
session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.PUBLISHING, function (stream) { setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); onPublishing(stream); ... }).publish(); |
5. Sending audio-video stream via WebRTC
6. Stopping publishing the stream.
stream.stop(); code
function onPublishing(stream) { $("#publishBtn").text("Stop").off('click').click(function () { $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); $("#publishInfo").text(""); } |
7. Receiving from the server an even confirming successful unpublishing.
StreamStatusEvent, статус UNPUBLISHED code
session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, receiveVideo: false, receiveAudio: false ... }).on(STREAM_STATUS.UNPUBLISHED, function () { setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED); onUnpublished(); ... }).publish(); |
When stream is captured from webcamera it is possible to play a local video in a browser-based VR player, Delight Player for example. This way stream can be played in virtual and mixed reality devices if one of browsers supporteds work on this device. To integrate a custom player JavaScript and HTML5 features are used.
1. For test we use:
2. Set stream name test and press Publish. Stream published is played in Delight player
1. Declaration of video element to play the stream, stream name input field and Publish/Unpublish buttons
<div style="width: 50%;"> <dl8-live-video id="remoteVideo" format="STEREO_TERPON" muted="true"> <source> </dl8-live-video> </div> <input class="form-control" type="text" id="streamName" placeholder="Stream Name"> <button id="publishBtn" type="button" class="btn btn-default" disabled>Publish</button> <button id="unpublishBtn" type="button" class="btn btn-default" disabled>UnPublish</button> |
2. Player readiness event handling
document.addEventListener('x-dl8-evt-ready', function () { dl8video = $('#remoteVideo').get(0); $('#publishBtn').prop('disabled', false).click(function() { publishStream(); }); }); |
3. Creating mock elements to play a stream
var mockLocalDisplay = $('<div></div>'); var mockLocalVideo = $('<video></video>',{id:'mock-LOCAL_CACHED_VIDEO'}); mockLocalDisplay.append(mockLocalVideo); |
4. Establishing connection to the server and stream creation
var video = dl8video.contentElement; Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { var session = Flashphoner.getSessions()[0]; session.createStream({ name: $('#streamName').val(), display: mockLocalDisplay.get(0) }).on(STREAM_STATUS.PUBLISHING, function (stream) { ... }).publish(); }) |
5. Publishing stream, playback start in VR player and Unpublish button handling
... }).on(STREAM_STATUS.PUBLISHING, function (stream) { var srcObject = mockLocalVideo.get(0).srcObject; video.srcObject = srcObject; dl8video.start(); mockLocalVideo.get(0).pause(); mockLocalVideo.get(0).srcObject = null; $('#unpublishBtn').prop('disabled', false).click(function() { stream.stop(); $('#publishBtn').prop('disabled', false); $('#unpublishBtn').prop('disabled', true); dl8video.exit(); }); }).publish(); |
Full source code of the sample VR player page
<!DOCTYPE html> <html> <head> <title>WebRTC Delight</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="../../dependencies/jquery/jquery-1.12.0.js"></script> <script type="text/javascript" src="../../dependencies/js/utils.js"></script> <script src="dl8-66b250447635476d123a44a391c80b09887e831e.js" async></script> <meta name="dl8-custom-format" content='{"name": "STEREO_TERPON","base":"STEREO_MESH","params":{"uri": "03198702.json"}}'> </head> <body> <div style="width: 50%;"> <dl8-live-video id="remoteVideo" format="STEREO_TERPON" muted="true"> <source> </dl8-live-video> </div> <input class="form-control" type="text" id="streamName" placeholder="Stream Name"> <button id="publishBtn" type="button" class="btn btn-default" disabled>Publish</button> <button id="unpublishBtn" type="button" class="btn btn-default" disabled>UnPublish</button> <script> Flashphoner.init({flashMediaProviderSwfLocation: '../../../../media-provider.swf'}); var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var STREAM_STATUS_INFO = Flashphoner.constants.STREAM_STATUS_INFO; var publishBtn = $('#publishBtn').get(0); var dl8video = null; var url = setURL(); document.addEventListener('x-dl8-evt-ready', function () { dl8video = $('#remoteVideo').get(0); $('#publishBtn').prop('disabled', false).click(function() { publishStream(); }); }); var mockLocalDisplay = $('<div></div>'); var mockLocalVideo = $('<video></video>',{id:'mock-LOCAL_CACHED_VIDEO'}); mockLocalDisplay.append(mockLocalVideo); function publishStream() { $('#publishBtn').prop('disabled', true); $('#unpublishBtn').prop('disabled', false); var video = dl8video.contentElement; Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { var session = Flashphoner.getSessions()[0]; session.createStream({ name: $('#streamName').val(), display: mockLocalDisplay.get(0) }).on(STREAM_STATUS.PUBLISHING, function (stream) { var srcObject = mockLocalVideo.get(0).srcObject; video.srcObject = srcObject; dl8video.start(); mockLocalVideo.get(0).pause(); mockLocalVideo.get(0).srcObject = null; $('#unpublishBtn').prop('disabled', false).click(function() { stream.stop(); $('#publishBtn').prop('disabled', false); $('#unpublishBtn').prop('disabled', true); dl8video.exit(); }); }).publish(); }) } </script> </body> </html> |
1. If the web app is inside an iframe element, publishing of the video stream may fail.
Symptoms: IceServer errors in the browser console.
Solution: put the app out of iframe to an individual page.
2. If publishing of the stream goes under Windows 10 or Windows 8 and hardware acceleration is enabled in the Google Chrome browser, bitrate problems are possible.
Symptoms: low quality of the video, muddy picture, bitrate shown in chrome://webrtc-internals is less than 100 kbps.
Solution: turn off hardware acceleration in the browser, switch the browser of the server to use the VP8 codec.
3. Stream publishing with local video playback in Delight Player does not work in MS Edge
Symptoms: when stream is published in MS Edge, local video playback does not start in Delight Player
Solution: use another browser to publish a stream
4. In some cases microphone does not work in Chrome browser while publishing WebRTC stream.
Symptoms: michrophone does not work while publishing WebRTC stream, including example web applications out of the box
Solution: turn off gain node creation in Chrome browser using WebSDK initialization parameter createMicGainNode: false
Flashphoner.init({ flashMediaProviderSwfLocation: '../../../../media-provider.swf', createMicGainNode: false }); |
Note that microphone gain setting will not work in this case.