...
To analyze the code, let's take the version of file manager.js, which is available here and can be downloaded with corresponding build 0.5.28.27472753.126.
1. Initialization of the API.
Flashphoner.init() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init({ screenSharingExtensionId: extensionId, flashMediaProviderSwfLocation: '../../../../media-provider.swf', mediaProvidersReadyCallback: function (mediaProviders) { //hide remote video if current media provider is Flash if (mediaProviders[0] == "Flash") { $("#fecForm").hide(); $("#stereoForm").hide(); $("#sendAudioBitrateForm").hide(); $("#cpuOveruseDetectionForm").hide(); } if (Flashphoner.isUsingTemasys()) { $("#audioInputForm").hide(); $("#videoInputForm").hide(); } } }) |
2. List available input media devices.
Flashphoner.getMediaDevices() кодcode
When input media devices are listed, drop-down lists of microphones and cameras on client page are filled.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaDevices(null, true).then(function (list) { list.audio.forEach(function (device) { ... }); list.video.forEach(function (device) { ... }); ... }).catch(function (error) { $("#notifyFlash").text("Failed to get media devices"); }); |
3. Get audio and video publishing constraints from client page
getConstraints() code
Publishing sources:
- camera (sendVideo)
- microphone (sendAudio)
- HTML5 Canvas (sendCanvasStream)
...
List available output media devices
Flashphoner.getMediaDevices() code
When output media devices are listed, drop-down lists of spakers and headphones on client page are filled.
Code Block | ||||
---|---|---|---|---|
| ||||
constraints = Flashphoner.getMediaDevices(null, true, MEDIA_DEVICE_KIND.OUTPUT).then(function (list) { audio: $("#sendAudio").is(':checked'),list.audio.forEach(function (device) { video: $("#sendVideo").is(':checked'), ... }); ... }).catch(function (error) { customStream: $("#sendCanvasStream#notifyFlash").is(':checked')text("Failed to get media devices"); }); |
Audio constraints:
...
4. Get audio and video publishing constraints from client page
getConstraints() code
Publishing sources:
- camera (sendVideo)
- microphone (sendAudio)
Code Block | ||||
---|---|---|---|---|
| ||||
if (constraints.audio)constraints = { constraints.audio = {: $("#sendAudio").is(':checked'), deviceIdvideo: $('#audioInput'"#sendVideo").valis(':checked'), }; |
Audio constraints:
- microphone choise (deviceId)
- error correction for Opus codec (fec)
- stereo mode (stereo)
- audio bitrate (bitrate)
Code Block | ||||
---|---|---|---|---|
| ||||
}; if (constraints.audio) { if ($(constraints.audio = { deviceId: $('#audioInput').val() }; if ($("#fec").is(':checked')) constraints.audio.fec = $("#fec").is(':checked'); if ($("#sendStereoAudio").is(':checked')) constraints.audio.stereo = $("#sendStereoAudio").is(':checked'); if (parseInt($('#sendAudioBitrate').val()) > 0) constraints.audio.bitrate = parseInt($('#sendAudioBitrate').val()); } |
...
Code Block | ||||
---|---|---|---|---|
| ||||
constraints.video = { deviceId: {exact: $('#videoInput').val()}, width: parseInt($('#sendWidth').val()), height: parseInt($('#sendHeight').val()) }; if (Browser.isSafariWebRTC() && Browser.isiOS() && Flashphoner.getMediaProviders()[0] === "WebRTC") { constraints.video.widthdeviceId = {minexact: parseInt($('#sendWidth#videoInput').val()), max: 640}; } constraints.video.height = {min: parseInt($('#sendHeight').val()), max: 480}; } if (parseInt($('#sendVideoMinBitrate').val()) > 0) constraints.video.minBitrate = parseInt($('#sendVideoMinBitrate').val()); if (parseInt($('#sendVideoMaxBitrate').val()) > 0) constraints.video.maxBitrate = parseInt($('#sendVideoMaxBitrate').val()); if (parseInt($('#fps').val()) > 0) constraints.video.frameRate = parseInt($('#fps').val()); |
45. Get access to media devices for local test
Flashphoner.getMediaAccess() code
Audio and video constraints and <div>-element to display captured video are passed to the method.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.getMediaAccess(getConstraints(), localVideo).then(function (disp) { $("#testBtn").text("Release").off('click').click(function () { $(this).prop('disabled', true); stopTest(); }).prop('disabled', false); ... testStarted = true; }).catch(function (error) { $("#testBtn").prop('disabled', false); testStarted = false; }); |
56. Connecting to the server
Flashphoner.createSession() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { //session connected, start streamingsetStatus("#connectStatus", session.status()); startStreamingonConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { setStatus("#connectStatus", SESSION_STATUS.DISCONNECTED); onStoppedonDisconnected(); }).on(SESSION_STATUS.FAILED, function () { setStatus("#connectStatus", SESSION_STATUS.FAILED); onStoppedonDisconnected(); }); |
67. Receiving the event confirming successful connection
ConnectionStatusEvent ESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.createSession({urlServer: url, timeout: tm}).on(SESSION_STATUS.ESTABLISHED, function (session) { //session connected, start streamingsetStatus("#connectStatus", session.status()); startStreamingonConnected(session); }).on(SESSION_STATUS.DISCONNECTED, function () { ... }).on(SESSION_STATUS.FAILED, function () { ... }); |
...
; |
8. Stream publishing
session.createStream(), publishStream.publish() code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true, constraints: constraints, mediaConnectionConstraints: mediaConnectionConstraints, ...sdpHook: rewriteSdp, }); transport: transportInput, publishStream.publish(); |
...
cvoExtension: cvo,
stripCodecs: strippedCodecs
...
});
publishStream.publish(); |
9. Receiving the event confirming successful streaming
StreamStatusEvent PUBLISHING code
On receiving the event, the preview stream is created with session.createStream() method and play() is called to play it
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ ({ ... }).on(STREAM_STATUS.PUBLISHING, function (stream) { $("#testBtn").prop('disabled', true); var video = document.getElementById(stream.id()); //resize local if resolution is available if (video.videoWidth > 0 && video.videoHeight > 0) { resizeLocalVideo({target: video}); } enablePublishToggles(true); if ($("#muteVideoToggle").is(":checked")) { muteVideo(); } if ($("#muteAudioToggle").is(":checked")) { muteAudio(); } //remove resize listener in case this video was cached earlier video.removeEventListener('resize', resizeLocalVideo); video.addEventListener('resize', resizeLocalVideo); publishStream.setMicrophoneGain(currentGainValue); setStatus("#publishStatus", STREAM_STATUS.PUBLISHING); onPublishing(stream); }).on(STREAM_STATUS.UNPUBLISHED, function () { ... }).on(STREAM_STATUS.FAILED, function () { ... }); publishStream.publish(); |
10. Stream playback
session.createStream(), previewStream.play() code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({
name: streamName,
display: remoteVideo,
constraints: constraints,
transport: transportOutput,
stripCodecs: strippedCodecs
...
});
previewStream.play(); |
11. Receiving the event confirming successful playback
StreamStatusEvent PLAYING code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({
...
}).on(STREAM_STATUS.PLAYING, function (stream) {
playConnectionQualityStat.connectionQualityUpdateTimestamp = new Date().valueOf();
setStatus("#playStatus", stream.status());
onPlaying(stream);
document.getElementById(stream.id()).addEventListener('resize', function (event) {
$("#playResolution").text(event.target.videoWidth + "x" + event.target.videoHeight);
resizeVideo(event.target);
});
//wait for incoming stream
if (Flashphoner.getMediaProviders()[0] == "WebRTC") {
setTimeout(function () {
detectSpeech(stream);
}, 3000);
}
...
});
previewStream.play(); |
12. Stop stream playback
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
$("#playBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false); |
13. Receiving the event confirming successful playback stop
StreamStatusEvent STOPPED code
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream = session.createStream({
...
}).on(STREAM_STATUS.STOPPED, function () {
setStatus("#playStatus", STREAM_STATUS.STOPPED);
onStopped();
...
});
previewStream.play(); |
14. Stop stream publishing
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
$("#publishBtn").text("Stop").off('click').click(function () {
$(this).prop('disabled', true);
stream.stop();
}).prop('disabled', false); |
15. Receiving the event confirming successful publishsing stop
StreamStatusEvent UNPUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({
...
}).on(STREAM_STATUS.UNPUBLISHED, function () {
setStatus("#publishStatus", STREAM_STATUS.UNPUBLISHED);
onUnpublished();
...
});
publishStream.publish(); |
16. Mute publisher audio
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteAudioToggle").is(":checked")) {
muteAudio();
} |
17. Mute publisher video
code:
Code Block | ||||
---|---|---|---|---|
| ||||
if ($("#muteVideoToggle").is(":checked")) {
muteVideo();
} |
18. Show WebRTC stream publishing statistics
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.getStats(function (stats) { if (stats && stats.outboundStream) { if (stats.outboundStream.video) { showStat(stats.outboundStream.video, "outVideoStat"); let vBitrate = (stats.outboundStream.video.bytesSent - videoBytesSent) * 8; if ($('#outVideoStatBitrate').length == 0) { let html = "<div>Bitrate: " + "<span id='outVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; $("#outVideoStat").append(html); } else { name: streamName, display: localVideo, $('#outVideoStatBitrate').text(vBitrate); cacheLocalResources: true, constraints: constraints, } mediaConnectionConstraints: mediaConnectionConstraints }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { videoBytesSent $("#testBtn").prop('disabled', true)= stats.outboundStream.video.bytesSent; var video = document.getElementById(publishStream.id()); //resize local if resolution is available ... if (video.videoWidth > 0} && video.videoHeight > 0) { if resizeLocalVideo({target: video}); stats.outboundStream.audio) { } enableMuteToggles(trueshowStat(stats.outboundStream.audio, "outAudioStat"); if ($("#muteVideoToggle").is(":checked")) { let aBitrate muteVideo(); = (stats.outboundStream.audio.bytesSent - audioBytesSent) * 8; } if ($("#muteAudioToggle").is(":checked")'#outAudioStatBitrate').length == 0) { muteAudio(); } let html = //remove resize listener in case this video was cached earlier video.removeEventListener('resize', resizeLocalVideo)"<div>Bitrate: " + "<span id='outAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; video.addEventListener('resize', resizeLocalVideo); setStatus(STREAM_STATUS.PUBLISHING); //play preview$("#outAudioStat").append(html); var constraints = { } audio: $("#playAudio").is(':checked'), else { video: $("#playVideo"'#outAudioStatBitrate').is(':checked')text(aBitrate); }; if (constraints.video) {} constraints.video = { audioBytesSent = stats.outboundStream.audio.bytesSent; width: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveWidth').val()) : 0, } } height: (!$("#receiveDefaultSize").is(":checked")) ? parseInt($('#receiveHeight').val()) : 0, ... }); |
19. Show WebRTC stream playback statistics
stream.getStats() code:
Code Block | ||||
---|---|---|---|---|
| ||||
previewStream.getStats(function (stats) { bitrate: (!$("#receiveDefaultBitrate").is(":checked")) ? $("#receiveBitrate").val() : 0, if (stats && stats.inboundStream) { quality:if (!$("#receiveDefaultQuality").is(":checked")) ? $('#quality').val() : 0stats.inboundStream.video) { }; } previewStream = session.createStream({ showStat(stats.inboundStream.video, "inVideoStat"); name: streamName, let vBitrate display: remoteVideo, = (stats.inboundStream.video.bytesReceived - videoBytesReceived) * 8; constraints: constraints ... });if ($('#inVideoStatBitrate').length == 0) { previewStream.play(); }).on(STREAM_STATUS.UNPUBLISHED, function () { ... let html = }).on(STREAM_STATUS.FAILED, function () { ... }); publishStream.publish(); |
9. Preview stream playback stop
previewStream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
$("#publishBtn").text("Stop").off('click').click(function () { "<div>Bitrate: " + "<span id='inVideoStatBitrate' style='font-weight: normal'>" + vBitrate + "</span>" + "</div>"; $(this).prop('disabled', true"#inVideoStat").append(html); previewStream.stop(); }).prop('disabled', false); |
10. Receiving the event confirming successful playback stop
StreamStatusEvent STOPPED code
Code Block | ||||
---|---|---|---|---|
| ||||
} previewStream = session.createStream(else { name: streamName, display: remoteVideo,$('#inVideoStatBitrate').text(vBitrate); constraints: constraints }).on(STREAM_STATUS.PLAYING, function (previewStream) { } ... videoBytesReceived }).on(STREAM_STATUS.STOPPED, function () {= stats.inboundStream.video.bytesReceived; publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { ... ...} }); if previewStream.play(); |
11. Streaming stop after preview playback stopped
publishStream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
(stats.inboundStream.audio) { previewStream = session.createStream({ showStat(stats.inboundStream.audio, "inAudioStat"); name: streamName, let aBitrate = (stats.inboundStream.audio.bytesReceived - audioBytesReceived) display: remoteVideo,* 8; constraints: constraints }).on(STREAM_STATUS.PLAYING, function (previewStream) { ... if ($('#inAudioStatBitrate').length == 0) { }).on(STREAM_STATUS.STOPPED, function () { let publishStream.stop(); }).on(STREAM_STATUS.FAILED, function () { html = "<div style='font-weight: bold'>Bitrate: " + "<span id='inAudioStatBitrate' style='font-weight: normal'>" + aBitrate + "</span>" + "</div>"; ... }); previewStream.play(); |
12. Receiving the event confirming successful streaming stop
StreamStatusEvent UNPUBLISHED код
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream = session.createStream({ $("#inAudioStat").append(html); name: streamName, } display:else localVideo, { cacheLocalResources: true, constraints: constraints, $('#inAudioStatBitrate').text(aBitrate); mediaConnectionConstraints: mediaConnectionConstraints }).on(STREAM_STATUS.PUBLISHING, function (publishStream) { } ... }).on(STREAM_STATUS.UNPUBLISHED, function () { audioBytesReceived setStatus(STREAM_STATUS.UNPUBLISHED)= stats.inboundStream.audio.bytesReceived; //enable start button } onStopped(); }).on(STREAM_STATUS.FAILED, function () { ... }); } publishStream.publish( }); |