This commit is contained in:
Iñaki Baz Castillo
2017-11-02 16:38:52 +01:00
parent 625f20f547
commit 52acf81eff
100 changed files with 26907 additions and 10234 deletions
+401
View File
@@ -0,0 +1,401 @@
/* eslint-disable key-spacing */
exports.ROOM_OPTIONS =
{
requestTimeout: 10000,
transportOptions:
{
tcp: false
},
__turnServers:
[
{
urls: [ 'turn:worker2.versatica.com:3478?transport=udp' ],
username: 'testuser1',
credential: 'testpasswd1'
}
],
hidden: false
};
exports.ROOM_RTP_CAPABILITIES =
{
codecs:
[
{
name: 'PCMA',
mimeType: 'audio/PCMA',
kind: 'audio',
clockRate: 8000,
preferredPayloadType: 8,
rtcpFeedback: [],
parameters: {}
},
{
name: 'opus',
mimeType: 'audio/opus',
kind: 'audio',
clockRate: 48000,
channels: 2,
preferredPayloadType: 96,
rtcpFeedback: [],
parameters: {}
},
{
name: 'SILK',
mimeType: 'audio/SILK',
kind: 'audio',
clockRate: 16000,
preferredPayloadType: 97,
rtcpFeedback: [],
parameters: {}
},
{
name: 'VP9',
mimeType: 'video/VP9',
kind: 'video',
clockRate: 90000,
preferredPayloadType: 102,
rtcpFeedback:
[
{
parameter: '',
type: 'nack'
},
{
parameter: 'pli',
type: 'nack'
},
{
parameter: '',
type: 'goog-remb'
},
{
parameter: 'bar',
type: 'foo'
}
],
parameters: {}
},
{
name: 'rtx',
mimeType: 'video/rtx',
kind: 'video',
clockRate: 90000,
preferredPayloadType: 103,
rtcpFeedback: [],
parameters: {
apt: 102
}
},
{
name: 'VP8',
mimeType: 'video/VP8',
kind: 'video',
clockRate: 90000,
preferredPayloadType: 100,
rtcpFeedback:
[
{
parameter: '',
type: 'nack'
},
{
parameter: 'pli',
type: 'nack'
},
{
parameter: '',
type: 'goog-remb'
},
{
parameter: 'bar',
type: 'foo'
}
],
parameters: {}
},
{
name: 'rtx',
mimeType: 'video/rtx',
kind: 'video',
clockRate: 90000,
preferredPayloadType: 101,
rtcpFeedback: [],
parameters: {
apt: 100
}
}
],
headerExtensions: [
{
kind: 'audio',
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
preferredId: 10
},
{
kind: 'video',
uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
preferredId: 11
},
{
kind: 'video',
uri: 'http://foo.bar',
preferredId: 12
}
],
fecMechanisms: []
};
exports.QUERY_ROOM_RESPONSE =
{
rtpCapabilities: exports.ROOM_RTP_CAPABILITIES
};
exports.JOIN_ROOM_RESPONSE =
{
peers:
[
{
name: 'alice',
appData: 'Alice iPad Pro',
consumers:
[
{
id: 3333,
kind: 'audio',
paused: false,
appData: 'ALICE_MIC',
rtpParameters:
{
muxId: null,
codecs:
[
{
name: 'PCMA',
mimeType: 'audio/PCMA',
clockRate: 8000,
payloadType: 8,
rtcpFeedback: [],
parameters: {}
}
],
headerExtensions:
[
{
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
id: 1
}
],
encodings:
[
{
ssrc: 33333333
}
],
rtcp:
{
cname: 'ALICECNAME',
reducedSize: true,
mux: true
}
}
}
]
},
{
name: 'bob',
appData: 'Bob HP Laptop',
consumers:
[
{
id: 6666,
kind: 'audio',
paused: false,
appData: 'BOB_MIC',
rtpParameters:
{
muxId: null,
codecs:
[
{
name: 'opus',
mimeType: 'audio/opus',
clockRate: 48000,
channels: 2,
payloadType: 96,
rtcpFeedback: [],
parameters: {}
}
],
headerExtensions:
[
{
uri: 'urn:ietf:params:rtp-hdrext:ssrc-audio-level',
id: 1
}
],
encodings:
[
{
ssrc: 66666666
}
],
rtcp:
{
cname: 'BOBCNAME',
reducedSize: true,
mux: true
}
}
}
]
}
]
};
exports.CREATE_TRANSPORT_1_RESPONSE =
{
iceParameters:
{
usernameFragment: 'server-usernamefragment-12345678',
password: 'server-password-xxxxxxxx',
iceLite: true
},
iceCandidates:
[
{
foundation: 'F1',
priority: 1234,
ip: '1.2.3.4',
protocol: 'udp',
port: 9999,
type: 'host'
}
],
dtlsParameters:
{
fingerprints:
[
{
algorithm: 'sha-256',
value: 'FF:FF:39:66:A4:E2:66:60:30:18:A7:59:B3:AF:A5:33:58:5E:7F:69:A4:62:A6:D4:EB:9F:B7:42:05:35:FF:FF'
}
],
role: 'client'
}
};
exports.CREATE_TRANSPORT_2_RESPONSE =
{
iceParameters:
{
usernameFragment: 'server-usernamefragment-12345678',
password: 'server-password-xxxxxxxx',
iceLite: true
},
iceCandidates:
[
{
foundation: 'F1',
priority: 1234,
ip: '1.2.3.4',
protocol: 'udp',
port: 9999,
type: 'host'
}
],
dtlsParameters:
{
fingerprints:
[
{
algorithm: 'sha-256',
value: 'FF:FF:39:66:A4:E2:66:60:30:18:A7:59:B3:AF:A5:33:58:5E:7F:69:A4:62:A6:D4:EB:9F:B7:42:05:35:FF:FF'
}
],
role: 'auto'
}
};
exports.ALICE_WEBCAM_NEW_CONSUMER_NOTIFICATION =
{
method: 'newConsumer',
notification: true,
id: 4444,
peerName: 'alice',
kind: 'video',
paused: true,
appData: 'ALICE_WEBCAM',
rtpParameters:
{
muxId: null,
codecs:
[
{
name: 'VP8',
mimeType: 'video/VP8',
clockRate: 90000,
payloadType: 100,
rtcpFeedback:
[
{
parameter: '',
type: 'nack'
},
{
parameter: 'pli',
type: 'nack'
},
{
parameter: '',
type: 'goog-remb'
},
{
parameter: 'bar',
type: 'foo'
}
],
parameters: {}
},
{
name: 'rtx',
mimeType: 'video/rtx',
clockRate: 90000,
payloadType: 101,
rtcpFeedback: [],
parameters: {
apt: 100
}
}
],
headerExtensions:
[
{
kind: 'video',
uri: 'http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time',
id: 11
},
{
kind: 'video',
uri: 'http://foo.bar',
id: 12
}
],
encodings:
[
{
ssrc: 444444441,
rtx: {
ssrc: 444444442
}
}
],
rtcp:
{
cname: 'ALICECNAME',
reducedSize: true,
mux: true
}
}
};
+145
View File
@@ -0,0 +1,145 @@
const path = require('path');
const gulp = require('gulp');
const gutil = require('gulp-util');
const plumber = require('gulp-plumber');
const rename = require('gulp-rename');
const browserify = require('browserify');
const watchify = require('watchify');
const envify = require('envify/custom');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const eslint = require('gulp-eslint');
const browserSync = require('browser-sync');
const OUTPUT_DIR = 'output';
const APP_NAME = 'mediasoup-client-test';
// Node environment.
process.env.NODE_ENV = 'development';
function logError(error)
{
gutil.log(gutil.colors.red(error.stack));
}
gulp.task('lint', () =>
{
const src =
[
'gulpfile.js',
'**/*.js',
'**/*.jsx'
];
return gulp.src(src)
.pipe(plumber())
.pipe(eslint())
.pipe(eslint.format());
});
gulp.task('html', () =>
{
return gulp.src('index.html')
.pipe(gulp.dest(OUTPUT_DIR));
});
gulp.task('bundle', () =>
{
const watch = true;
let bundler = browserify(
{
entries : 'index.jsx',
extensions : [ '.js', '.jsx' ],
// required for sourcemaps (must be false otherwise).
debug : process.env.NODE_ENV === 'development',
// required for watchify.
cache : {},
// required for watchify.
packageCache : {},
// required to be true only for watchify.
fullPaths : watch
})
.transform('babelify',
{
presets : [ 'es2015', 'es2017', 'react' ],
plugins :
[
'transform-runtime',
'transform-object-assign',
'transform-object-rest-spread'
]
})
.transform(envify(
{
NODE_ENV : process.env.NODE_ENV,
_ : 'purge'
}));
if (watch)
{
bundler = watchify(bundler);
bundler.on('update', () =>
{
const start = Date.now();
gutil.log('bundling...');
rebundle();
gutil.log('bundle took %sms', (Date.now() - start));
});
}
function rebundle()
{
return bundler.bundle()
.on('error', logError)
.pipe(plumber())
.pipe(source(`${APP_NAME}.js`))
.pipe(buffer())
.pipe(rename(`${APP_NAME}.js`))
.pipe(gulp.dest(OUTPUT_DIR));
}
return rebundle();
});
gulp.task('livebrowser', (done) =>
{
browserSync(
{
server :
{
baseDir : OUTPUT_DIR
},
ghostMode : false,
files : path.join(OUTPUT_DIR, '**', '*')
});
done();
});
gulp.task('watch', (done) =>
{
// Watch changes in HTML.
gulp.watch([ 'index.html' ], gulp.series(
'html'
));
// Watch changes in JS files.
gulp.watch([ 'gulpfile.js', '**/*.js', '**/*.jsx' ], gulp.series(
'lint'
));
done();
});
gulp.task('live', gulp.series(
'lint',
'html',
'bundle',
'watch',
'livebrowser'
));
gulp.task('default', gulp.series('live'));
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<title>mediasoup-client test</title>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no'>
<meta name='description' content='mediasoup-client test'>
<script async src='/mediasoup-client-test.js'></script>
</head>
<body>
<h1>mediasoup-client test</h1>
</body>
</html>
+692
View File
@@ -0,0 +1,692 @@
import * as mediasoupClient from 'mediasoup-client';
import domready from 'domready';
import Logger from '../lib/Logger';
const DATA = require('./DATA');
window.mediasoupClient = mediasoupClient;
const logger = new Logger();
const SEND = true;
const SEND_AUDIO = true;
const SEND_VIDEO = false;
const RECV = true;
domready(() =>
{
logger.debug('DOM ready');
run();
});
function run()
{
logger.debug('run() [environment:%s]', process.env.NODE_ENV);
let transport1;
let transport2;
let audioTrack;
let videoTrack;
let audioProducer1;
let audioProducer2;
let videoProducer;
logger.debug('calling room = new mediasoupClient.Room()');
// const room = new mediasoupClient.Room();
const room = new mediasoupClient.Room(DATA.ROOM_OPTIONS);
window.room = room;
room.on('closed', (originator, appData) =>
{
logger.warn(
'room "closed" event [originator:%s, appData:%o]', originator, appData);
});
room.on('request', (request, callback, errback) =>
{
logger.warn('sending request [method:%s]:%o', request.method, request);
switch (request.method)
{
case 'queryRoom':
{
setTimeout(() =>
{
callback(DATA.QUERY_ROOM_RESPONSE);
errback('upppps');
}, 200);
break;
}
case 'joinRoom':
{
setTimeout(() =>
{
callback(DATA.JOIN_ROOM_RESPONSE);
// errback('upppps');
}, 200);
break;
}
case 'createTransport':
{
setTimeout(() =>
{
switch (request.appData)
{
case 'TRANSPORT_1':
callback(DATA.CREATE_TRANSPORT_1_RESPONSE);
break;
case 'TRANSPORT_2':
callback(DATA.CREATE_TRANSPORT_2_RESPONSE);
break;
default:
errback('upppps');
}
}, 250);
break;
}
case 'createProducer':
{
setTimeout(() =>
{
callback();
}, 250);
break;
}
case 'enableConsumer':
{
setTimeout(() =>
{
callback();
}, 500);
break;
}
default:
errback(`NO IDEA ABOUT REQUEST METHOD "${request.method}"`);
}
});
room.on('notify', (notification) =>
{
logger.warn(
'sending notification [method:%s]:%o', notification.method, notification);
switch (notification.method)
{
case 'leaveRoom':
case 'updateTransport':
case 'closeTransport':
case 'closeProducer':
case 'pauseProducer':
case 'resumeProducer':
case 'pauseConsumer':
case 'resumeConsumer':
break;
default:
logger.error(`NO IDEA ABOUT NOTIFICATION METHOD "${notification.method}"`);
}
});
room.on('newpeer', (peer) =>
{
logger.warn('room "newpeer" event [name:"%s", peer:%o]', peer.name, peer);
handlePeer(peer);
});
Promise.resolve()
.then(() =>
{
logger.debug('calling room.join()');
const deviceInfo = mediasoupClient.getDeviceInfo();
const appData =
{
device : `${deviceInfo.name} ${deviceInfo.version}`
};
return room.join(null, appData);
// return room.join(DATA.ROOM_RTP_CAPABILITIES, appData);
})
.then((peers) =>
{
if (!RECV)
return;
logger.debug('room.join() succeeded');
logger.debug('calling transport2 = room.createTransport("recv")');
transport2 = room.createTransport('recv', 'TRANSPORT_2');
window.transport2 = transport2;
window.pc2 = transport2._handler._pc;
handleTransport(transport2);
for (const peer of peers)
{
handlePeer(peer);
}
})
.then(() =>
{
if (!SEND)
return;
if (room.canSend('audio'))
logger.debug('can send audio');
else
logger.warn('cannot send audio');
if (room.canSend('video'))
logger.debug('can send video');
else
logger.warn('cannot send video');
logger.debug('calling transport1 = room.createTransport("send")');
transport1 = room.createTransport('send', 'TRANSPORT_1');
window.transport1 = transport1;
window.pc1 = transport1._handler._pc;
handleTransport(transport1);
logger.debug('calling getUserMedia()');
return navigator.mediaDevices
.getUserMedia({ audio: SEND_AUDIO, video: SEND_VIDEO });
})
.then((stream) =>
{
if (!SEND)
return;
audioTrack = stream.getAudioTracks()[0];
videoTrack = stream.getVideoTracks()[0];
window.audioTrack = audioTrack;
window.videoTrack = videoTrack;
})
// Add Producers.
.then(() =>
{
if (audioTrack)
{
const deviceId = audioTrack.getSettings().deviceId;
logger.debug('calling audioProducer1 = room.createProducer(audioTrack)');
try
{
audioProducer1 = room.createProducer(audioTrack, `${deviceId}-1`);
window.audioProducer1 = audioProducer1;
handleProducer(audioProducer1);
}
catch (error)
{
logger.error(error);
}
logger.debug('calling audioProducer2 = room.createProducer(audioTrack)');
try
{
audioProducer2 = room.createProducer(audioTrack, `${deviceId}-2`);
window.audioProducer2 = audioProducer2;
handleProducer(audioProducer2);
}
catch (error)
{
logger.error(error);
}
}
if (videoTrack)
{
const deviceId = videoTrack.getSettings().deviceId;
logger.debug('calling videoProducer = room.createProducer(videoTrack)');
try
{
videoProducer = room.createProducer(videoTrack, `${deviceId}-1`);
window.videoProducer = videoProducer;
handleProducer(videoProducer);
}
catch (error)
{
logger.error(error);
}
}
})
// Receive notifications.
.then(() =>
{
if (!RECV)
return;
setTimeout(() =>
{
room.receiveNotification(DATA.ALICE_WEBCAM_NEW_CONSUMER_NOTIFICATION);
}, 2000);
});
}
function handleTransport(transport)
{
logger.warn(
'handleTransport() [direction:%s, appData:"%s", transport:%o]',
transport.direction, transport.appData, transport);
transport.on('closed', (originator, appData) =>
{
logger.warn(
'transport "closed" event [originator:%s, appData:%o, transport:%o]',
originator, appData, transport);
});
transport.on('connectionstatechange', (state) =>
{
logger.warn(
'transport "connectionstatechange" event [direction:%s, state:%s, transport:%o]',
transport.direction, state, transport);
});
setInterval(() =>
{
const queue = transport._commandQueue._queue;
if (queue.length !== 0)
logger.error('queue not empty [transport:%o, queue:%o]', transport, queue);
}, 15000);
}
function handlePeer(peer)
{
logger.warn('handlePeer() [name:"%s", peer:%o]', peer.name, peer);
switch (peer.name)
{
case 'alice':
window.alice = peer;
break;
case 'bob':
window.bob = peer;
break;
}
for (const consumer of peer.consumers)
{
handleConsumer(consumer);
}
peer.on('closed', (originator, appData) =>
{
logger.warn(
'peer "closed" event [name:"%s", originator:%s, appData:%o]',
peer.name, originator, appData);
});
peer.on('newconsumer', (consumer) =>
{
logger.warn(
'peer "newconsumer" event [name:"%s", id:%s, consumer:%o]',
peer.name, consumer.id, consumer);
handleConsumer(consumer);
});
}
function handleProducer(producer)
{
const transport1 = window.transport1;
logger.debug(
'handleProducer() [id:"%s", appData:%o, producer:%o]',
producer.id, producer.appData, producer);
logger.debug('handleProducer() | calling transport1.send(producer)');
transport1.send(producer)
.then(() =>
{
logger.debug('transport1.send(producer) succeeded');
})
.catch((error) =>
{
logger.error('transport1.send(producer) failed: %o', error);
});
producer.on('closed', (originator, appData) =>
{
logger.warn(
'producer "closed" event [id:%s, originator:%s, appData:%o, producer:%o]',
producer.id, originator, appData, producer);
});
producer.on('paused', (originator, appData) =>
{
logger.warn(
'producer "paused" event [id:%s, originator:%s, appData:%o, producer:%o]',
producer.id, originator, appData, producer);
});
producer.on('resumed', (originator, appData) =>
{
logger.warn(
'producer "resumed" event [id:%s, originator:%s, appData:%o, producer:%o]',
producer.id, originator, appData, producer);
});
producer.on('unhandled', () =>
{
logger.warn(
'producer "unhandled" event [id:%s, producer:%o]', producer.id, producer);
});
}
function handleConsumer(consumer)
{
const transport2 = window.transport2;
logger.debug(
'handleConsumer() [id:"%s", appData:%o, consumer:%o]',
consumer.id, consumer.appData, consumer);
switch (consumer.appData)
{
case 'ALICE_MIC':
window.aliceAudioConsumer = consumer;
break;
case 'ALICE_WEBCAM':
window.aliceVideoConsumer = consumer;
break;
case 'BOB_MIC':
window.bobAudioConsumer = consumer;
break;
}
logger.debug('handleConsumer() calling transport2.receive(consumer)');
transport2.receive(consumer)
.then((track) =>
{
logger.warn(
'transport2.receive(consumer) succeeded [track:%o]', track);
})
.catch((error) =>
{
logger.error('transport2.receive() failed:%o', error);
});
consumer.on('closed', (originator, appData) =>
{
logger.warn(
'consumer "closed" event [id:%s, originator:%s, appData:%o, consumer:%o]',
consumer.id, originator, appData, consumer);
});
consumer.on('paused', (originator, appData) =>
{
logger.warn(
'consumer "paused" event [id:%s, originator:%s, appData:%o, consumer:%o]',
consumer.id, originator, appData, consumer);
});
consumer.on('resumed', (originator, appData) =>
{
logger.warn(
'consumer "resumed" event [id:%s, originator:%s, appData:%o, consumer:%o]',
consumer.id, originator, appData, consumer);
});
consumer.on('unhandled', () =>
{
logger.warn(
'consumer "unhandled" event [id:%s, consumer:%o]', consumer.id, consumer);
});
}
// NOTE: Trigger server notifications.
window.notifyRoomClosed = function()
{
const room = window.room;
const notification =
{
method : 'roomClosed',
notification : true,
appData : 'ha cascao la room remota!!!'
};
room.receiveNotification(notification);
};
window.notifyTransportClosed = function()
{
const room = window.room;
const notification =
{
method : 'transportClosed',
notification : true,
id : room.transports[0].id,
appData : 'admin closed your transport'
};
room.receiveNotification(notification);
};
window.notifyAudioProducer1Closed = function()
{
const room = window.room;
const notification =
{
method : 'producerClosed',
notification : true,
id : window.audioProducer1.id,
appData : 'te paro el micro por la fuerza'
};
room.receiveNotification(notification);
};
window.notifyAudioProducer1Paused = function()
{
const room = window.room;
const notification =
{
method : 'producerPaused',
notification : true,
id : window.audioProducer1.id,
appData : 'te pause el micro por la fuerza'
};
room.receiveNotification(notification);
};
window.notifyAudioProducer1Resumed = function()
{
const room = window.room;
const notification =
{
method : 'producerResumed',
notification : true,
id : window.audioProducer1.id,
appData : 'te resumo el micro'
};
room.receiveNotification(notification);
};
window.notifyAlicePeerClosed = function()
{
const room = window.room;
const notification =
{
method : 'peerClosed',
notification : true,
name : 'alice',
appData : 'peer left'
};
room.receiveNotification(notification);
};
window.notifyAliceAudioConsumerClosed = function()
{
const room = window.room;
const notification =
{
method : 'consumerClosed',
notification : true,
peerName : 'alice',
id : 3333,
appData : 'mic broken'
};
room.receiveNotification(notification);
};
window.notifyAliceVideoConsumerClosed = function()
{
const room = window.room;
const notification =
{
method : 'consumerClosed',
notification : true,
peerName : 'alice',
id : 4444,
appData : 'webcam broken'
};
room.receiveNotification(notification);
};
window.notifyAliceVideoConsumerPaused = function()
{
const room = window.room;
const notification =
{
method : 'consumerPaused',
notification : true,
peerName : 'alice',
id : 4444,
appData : 'webcam paused'
};
room.receiveNotification(notification);
};
window.notifyAliceVideoConsumerResumed = function()
{
const room = window.room;
const notification =
{
method : 'consumerResumed',
notification : true,
peerName : 'alice',
id : 4444,
appData : 'webcam resumed'
};
room.receiveNotification(notification);
};
// NOTE: Test pause/resume.
window.testPauseResume = function()
{
logger.debug('testPauseResume() with audioProducer1');
const producer = window.audioProducer1;
// producer.once('paused', () =>
// {
// producer.resume('I RESUME TO FUACK!!!');
// });
logger.debug('testPauseResume() | (1) calling producer.pause()');
if (producer.pause('I PAUSE (1)'))
{
logger.warn(
'testPauseResume() | (1) producer.pause() succeeded [locallyPaused:%s]',
producer.locallyPaused);
}
else
{
logger.error(
'testPauseResume() | (1) producer.pause() failed [locallyPaused:%s]',
producer.locallyPaused);
}
logger.debug('testPauseResume() | (2) calling producer.pause()');
if (producer.pause('I PAUSE (2)'))
{
logger.warn(
'testPauseResume() | (2) producer.pause() succeeded [locallyPaused:%s]',
producer.locallyPaused);
}
else
{
logger.error(
'testPauseResume() | (2) producer.pause() failed [locallyPaused:%s]',
producer.locallyPaused);
}
logger.debug('testPauseResume() | (3) calling producer.resume()');
if (producer.resume('I RESUME (3)'))
{
logger.warn(
'testPauseResume() | (3) producer.resume() succeeded [locallyPaused:%s]',
producer.locallyPaused);
}
else
{
logger.error(
'testPauseResume() | (3) producer.resume() failed [locallyPaused:%s]',
producer.locallyPaused);
}
};
// NOTE: For debugging.
window.dump1 = function()
{
const transport1 = window.transport1;
const pc1 = transport1._handler._pc;
if (pc1 && pc1.localDescription)
logger.warn('PC1 SEND LOCAL OFFER:\n%s', pc1.localDescription.sdp);
if (pc1 && pc1.remoteDescription)
logger.warn('PC1 SEND REMOTE ANSWER:\n%s', pc1.remoteDescription.sdp);
};
window.dump2 = function()
{
const transport2 = window.transport2;
const pc2 = transport2._handler._pc;
if (pc2 && pc2.remoteDescription)
logger.warn('PC2 RECV REMOTE OFFER:\n%s', pc2.remoteDescription.sdp);
if (pc2 && pc2.localDescription)
logger.warn('PC2 RECV LOCAL ANSWER:\n%s', pc2.localDescription.sdp);
};
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<title>mediasoup-client test</title>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no'>
<meta name='description' content='mediasoup-client test'>
<script async src='/mediasoup-client-test.js'></script>
</head>
<body>
<h1>mediasoup-client test</h1>
</body>
</html>
File diff suppressed because one or more lines are too long