| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 | 
							- import { NativeModules } from 'react-native';
 - import { RTCPeerConnection, RTCSessionDescription } from 'react-native-webrtc';
 - 
 - // XXX At the time of this writing extending RTCPeerConnection using ES6 'class'
 - // and 'extends' causes a runtime error related to the attempt to define the
 - // onaddstream property setter. The error mentions that babelHelpers.set is
 - // undefined which appears to be a thing inside React Native's packager. As a
 - // workaround, extend using the pre-ES6 way.
 - 
 - /**
 -  * The RTCPeerConnection provided by react-native-webrtc fires onaddstream
 -  * before it remembers remotedescription (and thus makes it available to API
 -  * clients). Because that appears to be a problem for lib-jitsi-meet which has
 -  * been successfully running on Chrome, Firefox, Temasys, etc. for a very long
 -  * time, attempt to meets its expectations (by extending RTCPPeerConnection).
 -  *
 -  * @class
 -  */
 - export default function _RTCPeerConnection(...args) {
 - 
 -     /* eslint-disable indent, no-invalid-this */
 - 
 -     RTCPeerConnection.apply(this, args);
 - 
 -     this.onaddstream = (...args) => // eslint-disable-line no-shadow
 -         (this._onaddstreamQueue
 -                 ? this._queueOnaddstream
 -                 : this._invokeOnaddstream)
 -             .apply(this, args);
 - 
 -     // Shadow RTCPeerConnection's onaddstream but after _RTCPeerConnection has
 -     // assigned to the property in question. Defining the property on
 -     // _RTCPeerConnection's prototype may (or may not, I don't know) work but I
 -     // don't want to try because the following approach appears to work and I
 -     // understand it.
 -     Object.defineProperty(this, 'onaddstream', {
 -         configurable: true,
 -         enumerable: true,
 -         get() {
 -             return this._onaddstream;
 -         },
 -         set(value) {
 -             this._onaddstream = value;
 -         }
 -     });
 - 
 -     /* eslint-enable indent, no-invalid-this */
 - }
 - 
 - _RTCPeerConnection.prototype = Object.create(RTCPeerConnection.prototype);
 - _RTCPeerConnection.prototype.constructor = _RTCPeerConnection;
 - 
 - _RTCPeerConnection.prototype._invokeOnaddstream = function(...args) {
 -     const onaddstream = this._onaddstream;
 - 
 -     return onaddstream && onaddstream.apply(this, args);
 - };
 - 
 - _RTCPeerConnection.prototype._invokeQueuedOnaddstream = function(q) {
 -     q && q.forEach(args => {
 -         try {
 -             this._invokeOnaddstream(...args);
 -         } catch (e) {
 -             // TODO Determine whether the combination of the standard
 -             // setRemoteDescription and onaddstream results in a similar
 -             // swallowing of errors.
 -             _LOGE(e);
 -         }
 -     });
 - };
 - 
 - _RTCPeerConnection.prototype._queueOnaddstream = function(...args) {
 -     this._onaddstreamQueue.push(Array.from(args));
 - };
 - 
 - _RTCPeerConnection.prototype.setRemoteDescription = function(
 -         sessionDescription,
 -         successCallback,
 -         errorCallback) {
 -     // If the deprecated callback-based version is used, translate it to the
 -     // Promise-based version.
 -     if (typeof successCallback !== 'undefined'
 -             || typeof errorCallback !== 'undefined') {
 -         // XXX Returning a Promise is not necessary. But I don't see why it'd
 -         // hurt (much).
 -         return (
 -             _RTCPeerConnection.prototype.setRemoteDescription.call(
 -                     this,
 -                     sessionDescription)
 -                 .then(successCallback, errorCallback));
 -     }
 - 
 -     return (
 -         _synthesizeIPv6Addresses(sessionDescription)
 -             .catch(reason => {
 -                 reason && _LOGE(reason);
 - 
 -                 return sessionDescription;
 -             })
 -             .then(value => _setRemoteDescription.bind(this)(value)));
 - 
 - };
 - 
 - /**
 -  * Logs at error level.
 -  *
 -  * @private
 -  * @returns {void}
 -  */
 - function _LOGE(...args) {
 -     console && console.error && console.error(...args);
 - }
 - 
 - /**
 -  * Adapts react-native-webrtc's {@link RTCPeerConnection#setRemoteDescription}
 -  * implementation which uses the deprecated, callback-based version to the
 -  * {@code Promise}-based version.
 -  *
 -  * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
 -  * which specifies the configuration of the remote end of the connection.
 -  * @private
 -  * @private
 -  * @returns {Promise}
 -  */
 - function _setRemoteDescription(sessionDescription) {
 -     return new Promise((resolve, reject) => {
 - 
 -         /* eslint-disable no-invalid-this */
 - 
 -         // Ensure I'm not remembering onaddstream invocations from previous
 -         // setRemoteDescription calls. I shouldn't be but... anyway.
 -         this._onaddstreamQueue = [];
 - 
 -         RTCPeerConnection.prototype.setRemoteDescription.call(
 -             this,
 -             sessionDescription,
 -             (...args) => {
 -                 let q;
 - 
 -                 try {
 -                     resolve(...args);
 -                 } finally {
 -                     q = this._onaddstreamQueue;
 -                     this._onaddstreamQueue = undefined;
 -                 }
 - 
 -                 this._invokeQueuedOnaddstream(q);
 -             },
 -             (...args) => {
 -                 this._onaddstreamQueue = undefined;
 - 
 -                 reject(...args);
 -             });
 - 
 -         /* eslint-enable no-invalid-this */
 -     });
 - }
 - 
 - /**
 -  * Synthesize IPv6 addresses on iOS in order to support IPv6 NAT64 networks.
 -  *
 -  * @param {RTCSessionDescription} sdp - The RTCSessionDescription which
 -  * specifies the configuration of the remote end of the connection.
 -  * @private
 -  * @returns {Promise}
 -  */
 - function _synthesizeIPv6Addresses(sdp) {
 -     // The synthesis of IPv6 addresses is implemented on iOS only at the time of
 -     // this writing.
 -     if (!NativeModules.POSIX) {
 -         return Promise.resolve(sdp);
 -     }
 - 
 -     return (
 -         new Promise(resolve => resolve(_synthesizeIPv6Addresses0(sdp)))
 -             .then(({ ips, lines }) =>
 -                 Promise.all(Array.from(ips.values()))
 -                     .then(() => _synthesizeIPv6Addresses1(sdp, ips, lines))
 -             ));
 - }
 - 
 - /* eslint-disable max-depth */
 - 
 - /**
 -  * Implements the initial phase of the synthesis of IPv6 addresses.
 -  *
 -  * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
 -  * for which IPv6 addresses will be synthesized.
 -  * @private
 -  * @returns {{
 -  *     ips: Map,
 -  *     lines: Array
 -  * }}
 -  */
 - function _synthesizeIPv6Addresses0(sessionDescription) {
 -     const sdp = sessionDescription.sdp;
 -     let start = 0;
 -     const lines = [];
 -     const ips = new Map();
 -     const getaddrinfo = NativeModules.POSIX.getaddrinfo;
 - 
 -     do {
 -         const end = sdp.indexOf('\r\n', start);
 -         let line;
 - 
 -         if (end === -1) {
 -             line = sdp.substring(start);
 - 
 -             // Break out of the loop at the end of the iteration.
 -             start = undefined;
 -         } else {
 -             line = sdp.substring(start, end);
 -             start = end + 2;
 -         }
 - 
 -         if (line.startsWith('a=candidate:')) {
 -             const candidate = line.split(' ');
 - 
 -             if (candidate.length >= 10 && candidate[6] === 'typ') {
 -                 const ip4s = [ candidate[4] ];
 -                 let abort = false;
 - 
 -                 for (let i = 8; i < candidate.length; ++i) {
 -                     if (candidate[i] === 'raddr') {
 -                         ip4s.push(candidate[++i]);
 -                         break;
 -                     }
 -                 }
 - 
 -                 for (const ip of ip4s) {
 -                     if (ip.indexOf(':') === -1) {
 -                         ips.has(ip)
 -                             || ips.set(ip, new Promise((resolve, reject) => {
 -                                 const v = ips.get(ip);
 - 
 -                                 if (v && typeof v === 'string') {
 -                                     resolve(v);
 -                                 } else {
 -                                     getaddrinfo(ip).then(
 -                                         value => {
 -                                             if (value.indexOf(':') === -1
 -                                                     || value === ips.get(ip)) {
 -                                                 ips.delete(ip);
 -                                             } else {
 -                                                 ips.set(ip, value);
 -                                             }
 -                                             resolve(value);
 -                                         },
 -                                         reject);
 -                                 }
 -                             }));
 -                     } else {
 -                         abort = true;
 -                         break;
 -                     }
 -                 }
 -                 if (abort) {
 -                     ips.clear();
 -                     break;
 -                 }
 - 
 -                 line = candidate;
 -             }
 -         }
 - 
 -         lines.push(line);
 -     } while (start);
 - 
 -     return {
 -         ips,
 -         lines
 -     };
 - }
 - 
 - /* eslint-enable max-depth */
 - 
 - /**
 -  * Implements the initial phase of the synthesis of IPv6 addresses.
 -  *
 -  * @param {RTCSessionDescription} sessionDescription - The RTCSessionDescription
 -  * for which IPv6 addresses are being synthesized.
 -  * @param {Map} ips - A Map of IPv4 addresses found in the specified
 -  * sessionDescription to synthesized IPv6 addresses.
 -  * @param {Array} lines - The lines of the specified sessionDescription.
 -  * @private
 -  * @returns {RTCSessionDescription} A RTCSessionDescription that represents the
 -  * result of the synthesis of IPv6 addresses.
 -  */
 - function _synthesizeIPv6Addresses1(sessionDescription, ips, lines) {
 -     if (ips.size === 0) {
 -         return sessionDescription;
 -     }
 - 
 -     for (let l = 0; l < lines.length; ++l) {
 -         const candidate = lines[l];
 - 
 -         if (typeof candidate !== 'string') {
 -             let ip4 = candidate[4];
 -             let ip6 = ips.get(ip4);
 - 
 -             ip6 && (candidate[4] = ip6);
 - 
 -             for (let i = 8; i < candidate.length; ++i) {
 -                 if (candidate[i] === 'raddr') {
 -                     ip4 = candidate[++i];
 -                     (ip6 = ips.get(ip4)) && (candidate[i] = ip6);
 -                     break;
 -                 }
 -             }
 - 
 -             lines[l] = candidate.join(' ');
 -         }
 -     }
 - 
 -     return new RTCSessionDescription({
 -         sdp: lines.join('\r\n'),
 -         type: sessionDescription.type
 -     });
 - }
 
 
  |