EasyRTC URL.createObjectURL returns a valid url but video element is black

739 views Asked by At

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 an ng-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>
0

There are 0 answers