This bug happens only when easyRTC is run inside an angularJS application.
Say we have 3 participants, A, B and C. When each one of them connects at random times, they see each other's video stream (3 concurrent streams). Say A refreshes the page. This is the sequence of events, simplified
easyrtc.setRoomOccupantListener
is fired- The occupants list is retrieved
- some other easyrtc events are fired
- for each occupant's stream a stream URL is generated calling
URL.createObjectURL
- the stream URL is added to a
video
element and the video stream is shown (via anng-repeat
method)
BUT
Although everything works as planned, A only sees the video from B. C has a valid stream URL but the video is black.
- This happens only when A refreshes the page and retrieves all the other streams in bulk.
- the video element lives inside a directive. I have tried to create the video element with jQuery, inside the directive, but no luck
- The directive has an isolate scope
- I use
$sce.trustAsResourceURL
for the stream URL - The same exact code works fine in an angular-free js sample file
- I have tried to save the streams to a
window.myStreamArray
and then in the console re-generate the stream URLs and add them to the video elements with some jQuery. This works fine for the 1st stream. The 2nd stream, although I get a valid url, nothing is shown - The video element has
autoplay
The code that runs standalone and works. the same code runs as an angular service. If we open 3 browser tabs and B taps 1.connect, C taps 2.connect and A taps 1.connect, A will receive B and C and display the video streams.
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<meta http-equiv='cache-control' content='no-cache'>
<meta http-equiv='expires' content='0'>
<meta http-equiv='pragma' content='no-cache'>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
</head>
<body>
<h1>easy rtc demo</h1>
<button id="btnOneInit">1. connect</button>
<button id="btnOne">1. start streaming</button>
<button id="btnTwoInit">2. connect</button>
<button id="btnTwo">2. start streaming</button>
<div id="one">
<video autoplay src=""></video>
</div>
<div id="two">
<video autoplay src=""></video>
</div>
<div id="three">
<video autoplay src=""></video>
</div>
<div id="four">
<video autoplay src=""></video>
</div>
<script src="/lib/jquery-2.1.0.min.js"></script>
<script src="/lib/sugar.min.js"></script>
<script src="/lib/socket.io.js"></script>
<script src="/lib/sails.io.js"></script>
<script src="/lib/easyrtc.js"></script>
<script>
function easy() {
var _localRoom, _remoteRoom, _localStream, _remoteStreams, _available , _webRTCinitialized, _user;
var _acceptedStreamsMap = {};
var roomName = "SectorOne";
var mySocket;
function init(options) {
_webRTCinitialized = false;
_available = false;
_remoteStreams = [];
_remoteRoom = {
id: undefined,
token: undefined
};
mySocket = io.connect();
_user = {
id: options.id,
role: 'presenter'
};
return this;
}
function connect() {
easyrtc.dontAddCloseButtons();
easyrtc.enableDebug(true);
easyrtc.enableAudio(false);
easyrtc.enableVideo(false);
easyrtc.enableAudioReceive(true);
easyrtc.enableVideoReceive(true);
easyrtc.setRoomOccupantListener(occupantsChanged);
easyrtc.connect('auvious.audioVideo', function (easyrtcid, roomOwner) {
_localRoom = easyrtcid;
console.log('connected with id : ' + easyrtcid);
}, function (error) {
alert(error);
});
easyrtc.setDisconnectListener(roomDisconnected);
easyrtc.setStreamAcceptor(callAccepted);
easyrtc.setAcceptChecker(acceptCall);
easyrtc.setOnStreamClosed(streamRemoved);
}
function startStreaming(audio, video, screen) {
easyrtc.enableAudio(audio);
easyrtc.enableVideo(video);
easyrtc.enableAudioReceive(true);
easyrtc.enableVideoReceive(true);
easyrtc.initMediaSource(
// success callback
function () {
var stream = easyrtc.getLocalStream();
var compositeID = _user.id;
var eventData = {userId: _user.id, role: _user.role, easyrtcid: _localRoom, id: stream.id, video: video, audio: !video, screen: screen};
_acceptedStreamsMap[_localRoom] = eventData;
var streamUrl = easyrtc.getLocalStreamAsUrl();
var options = {video: video, audio: !video, screen: screen, local: true, streamUrl: streamUrl};
console.log('about to show LOCAL stream...');
showStream(compositeID, stream, options);
easyrtc.setRoomApiField(roomName, _localRoom, JSON.stringify(eventData));
},
function (err) {
alert(err);
}
);
}
function occupantsChanged(roomName, occupants, selfInfo) {
_remoteStreams = [];
easyrtc.setRoomOccupantListener(null);
for (var easyrtcid in occupants) {
var occ = occupants[easyrtcid];
if (occ && occ.apiField) {
var streamingId = occ.apiField[easyrtcid];
if (streamingId && !_acceptedStreamsMap.hasOwnProperty(streamingId)) {
var data = JSON.parse(occ.apiField[easyrtcid].fieldValue);
_remoteStreams.push(data);
}
}
}
if (_remoteStreams.length > 0)
callOthers();
}
function callAccepted(easyrtcid, stream) {
if (_acceptedStreamsMap[easyrtcid] && !_acceptedStreamsMap[easyrtcid].streaming) {
var remoteStream = _acceptedStreamsMap[easyrtcid];
var compositeID = remoteStream.userId;
var options = Object.clone(remoteStream);
options.streamUrl = URL.createObjectURL(stream);
options.local = false;
console.log('about to show remote stream...');
console.log(_acceptedStreamsMap);
_acceptedStreamsMap[easyrtcid].streaming = true;
showStream(compositeID, stream, options);
}
}
function acceptCall(easyrtcid, acceptedCB) {
acceptedCB(true);
}
function callOthers() {
function establishConnection(position) {
function callSuccess() {
}
function callFailure(errorCode, errorText) {
}
if (position >= 0) {
console.log('calling ....' + _remoteStreams[position].easyrtcid);
easyrtc.call(_remoteStreams[position].easyrtcid, callSuccess, callFailure, function (accepted, easyrtcid) {
if (position > 0) {
establishConnection(position - 1);
}
connectionCallAccepted(accepted, easyrtcid)
})
}
}
if (_remoteStreams.length > 0) {
establishConnection(_remoteStreams.length - 1);
}
}
function connectionCallAccepted(accepted, easyrtcid) {
if (accepted) {
if (_acceptedStreamsMap[easyrtcid] == undefined) {
_acceptedStreamsMap[easyrtcid] = _remoteStreams.find(function (stream) {
return stream.easyrtcid == easyrtcid;
});
}
console.log('accepted stream--->' + easyrtcid);
console.log(_acceptedStreamsMap)
}
}
function showStream(compositeID, stream, options) {
$(compositeID).find('video').attr('src', options.streamUrl);
console.log('streaming...');
}
function closeStream(stream) {
easyrtc.enableAudio(false);
easyrtc.enableVideo(false);
if (stream.local) {
delete _acceptedStreamsMap[_localRoom];
easyrtc.setRoomApiField(roomName, _localRoom, undefined);
easyrtc.closeLocalStream(stream.streamName);
}
}
function streamRemoved(easyrtcId) {
easyrtc.enableAudio(false);
easyrtc.enableVideo(false);
var stream = _acceptedStreamsMap[easyrtcId];
if (stream) {
console.log('stream removed ---->' + easyrtcId);
console.log(_acceptedStreamsMap);
var attrs = {guid: stream.id, userId: stream.userId};
delete _acceptedStreamsMap[easyrtcId];
console.log(_acceptedStreamsMap);
}
}
function roomDisconnected() {
}
function disconnect() {
easyrtc.disconnect();
}
return {
init: init,
startStreaming: startStreaming,
connect: connect,
disconnect: disconnect,
closeStream: closeStream
}
}
var rtc = new easy();
$('#btnOneInit').click(function () {
rtc.init({id: "#one"});
rtc.connect();
})
$('#btnTwoInit').click(function () {
rtc.init({id: "#two"});
rtc.connect();
})
$('#btnOne, #btnTwo').click(function () {
rtc.startStreaming(false, true, false);
})
</script>
</body>
</html>