feat: w
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
dist/
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/yarn-error.log
|
||||
.DS_Store
|
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
12
.idea/learn-webrtc.iml
generated
Normal file
12
.idea/learn-webrtc.iml
generated
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
78
.idea/misc.xml
generated
Normal file
78
.idea/misc.xml
generated
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownProjectSettings" wasCopied="true">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
|
||||
<PanelProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
|
||||
</PanelProvider>
|
||||
</PreviewSettings>
|
||||
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
|
||||
<PegdownExtensions>
|
||||
<option name="ABBREVIATIONS" value="false" />
|
||||
<option name="ANCHORLINKS" value="true" />
|
||||
<option name="ASIDE" value="false" />
|
||||
<option name="ATXHEADERSPACE" value="true" />
|
||||
<option name="AUTOLINKS" value="true" />
|
||||
<option name="DEFINITIONS" value="false" />
|
||||
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
|
||||
<option name="FENCED_CODE_BLOCKS" value="true" />
|
||||
<option name="FOOTNOTES" value="false" />
|
||||
<option name="HARDWRAPS" value="false" />
|
||||
<option name="HTML_DEEP_PARSER" value="false" />
|
||||
<option name="INSERTED" value="false" />
|
||||
<option name="QUOTES" value="false" />
|
||||
<option name="RELAXEDHRULES" value="true" />
|
||||
<option name="SMARTS" value="false" />
|
||||
<option name="STRIKETHROUGH" value="true" />
|
||||
<option name="SUBSCRIPT" value="false" />
|
||||
<option name="SUPERSCRIPT" value="false" />
|
||||
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
|
||||
<option name="SUPPRESS_INLINE_HTML" value="false" />
|
||||
<option name="TABLES" value="true" />
|
||||
<option name="TASKLISTITEMS" value="true" />
|
||||
<option name="TOC" value="false" />
|
||||
<option name="WIKILINKS" value="true" />
|
||||
</PegdownExtensions>
|
||||
<ParserOptions>
|
||||
<option name="ADMONITION_EXT" value="false" />
|
||||
<option name="ATTRIBUTES_EXT" value="false" />
|
||||
<option name="COMMONMARK_LISTS" value="true" />
|
||||
<option name="DUMMY" value="false" />
|
||||
<option name="EMOJI_SHORTCUTS" value="true" />
|
||||
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
|
||||
<option name="FLEXMARK_FRONT_MATTER" value="false" />
|
||||
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
|
||||
<option name="GFM_TABLE_RENDERING" value="true" />
|
||||
<option name="GITBOOK_URL_ENCODING" value="false" />
|
||||
<option name="GITHUB_LISTS" value="false" />
|
||||
<option name="GITHUB_WIKI_LINKS" value="true" />
|
||||
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
|
||||
<option name="JEKYLL_FRONT_MATTER" value="false" />
|
||||
<option name="NO_TEXT_ATTRIBUTES" value="false" />
|
||||
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
|
||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
|
||||
</ParserOptions>
|
||||
</ParserSettings>
|
||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false">
|
||||
<GeneratorProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
|
||||
</GeneratorProvider>
|
||||
<headerTop />
|
||||
<headerBottom />
|
||||
<bodyTop />
|
||||
<bodyBottom />
|
||||
</HtmlSettings>
|
||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="true" isCssTextEnabled="false" isDynamicPageWidth="true">
|
||||
<StylesheetProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
|
||||
</StylesheetProvider>
|
||||
<ScriptProviders />
|
||||
<cssText />
|
||||
<cssUriHistory />
|
||||
</CssSettings>
|
||||
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
|
||||
<LinkMapSettings>
|
||||
<textMaps />
|
||||
</LinkMapSettings>
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/learn-webrtc.iml" filepath="$PROJECT_DIR$/.idea/learn-webrtc.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# learn-webrtc
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/sevenzyh/learn-webrtc.git
|
||||
|
||||
cd webrtc-server
|
||||
|
||||
yarn
|
||||
|
||||
npm start
|
||||
|
||||
cd webrtc-static
|
||||
|
||||
yarn
|
||||
|
||||
npm start
|
||||
|
||||
```
|
17
webrtc-server/package.json
Normal file
17
webrtc-server/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "webrtc-server",
|
||||
"version": "1.0.0",
|
||||
"description": "webrtc-demo-server",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"author": "ZhangYuhang",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"koa": "^2.13.0",
|
||||
"koa-send": "^5.0.1",
|
||||
"koa-static": "^5.0.0",
|
||||
"socket.io": "^2.3.0"
|
||||
}
|
||||
}
|
112
webrtc-server/server.js
Normal file
112
webrtc-server/server.js
Normal file
@ -0,0 +1,112 @@
|
||||
const Koa = require('koa');
|
||||
const koaSend = require('koa-send');
|
||||
const statics = require('koa-static');
|
||||
const socket = require('socket.io');
|
||||
|
||||
const path = require('path');
|
||||
const http = require('http');
|
||||
|
||||
const port = 3000;
|
||||
const app = new Koa();
|
||||
|
||||
|
||||
app.use(statics(
|
||||
path.join( __dirname, './dist')
|
||||
));
|
||||
app.use(async (ctx, next) => {
|
||||
if (!/\./.test(ctx.request.url)) {
|
||||
await koaSend(
|
||||
ctx,
|
||||
'index.html',
|
||||
{
|
||||
root: path.join(__dirname, './'),
|
||||
maxage: 1000 * 60 * 60 * 24 * 7,
|
||||
gzip: true,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
await next();
|
||||
}
|
||||
});
|
||||
const httpServer = http.createServer(app.callback()).listen(port, ()=>{
|
||||
console.log('httpServer app started at port ...' + port);
|
||||
});
|
||||
const options = {
|
||||
ioOptions: {
|
||||
pingTimeout: 10000,
|
||||
pingInterval: 5000,
|
||||
}
|
||||
};
|
||||
const httpIo = socket(httpServer, options);
|
||||
// Record<roomId, { userName: string; roomId: string; socketId: string }>
|
||||
const rooms = {};
|
||||
// Record<socketId, sock>
|
||||
const socks = {};
|
||||
const httpConnectIoCallBack = (sock) => {
|
||||
console.log(`sockId:${sock.id}连接成功!!!`);
|
||||
sock.emit('connectionSuccess', sock.id);
|
||||
// 用户断开连接
|
||||
sock.on('userLeave', ({ userName, roomId, sockId} = user)=> {
|
||||
console.log(`userName:${userName}, roomId:${roomId}, sockId:${sockId} 断开了连接...`);
|
||||
if (roomId && rooms[roomId] && rooms[roomId].length) {
|
||||
rooms[roomId] = rooms[roomId].filter(item => item.sockId!==sockId);
|
||||
httpIo.in(roomId).emit('userLeave', rooms[roomId]);
|
||||
console.log(`userName:${userName}, roomId:${roomId}, sockId:${sockId} 离开了房间...`);
|
||||
}
|
||||
});
|
||||
// 用户加入房间
|
||||
sock.on('checkRoom', ({ userName, roomId, sockId})=> {
|
||||
rooms[roomId] = rooms[roomId] || [];
|
||||
sock.emit('checkRoomSuccess', rooms[roomId]);
|
||||
if (rooms[roomId].length > 1) return false;
|
||||
rooms[roomId].push({ userName, roomId, sockId});
|
||||
sock.join(roomId, () => {
|
||||
httpIo.in(roomId).emit('joinRoomSuccess', rooms[roomId]);
|
||||
socks[sockId] = sock;
|
||||
console.log(`userName:${userName}, roomId:${roomId}, sockId:${sockId} 成功加入房间!!!`);
|
||||
});
|
||||
});
|
||||
// 发送视频
|
||||
sock.on('toSendVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('receiveVideo', user);
|
||||
});
|
||||
// 取消发送视频
|
||||
sock.on('cancelSendVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('cancelSendVideo', user);
|
||||
});
|
||||
// 接收视频邀请
|
||||
sock.on('receiveVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('receiveVideo', user);
|
||||
});
|
||||
// 拒绝接收视频
|
||||
sock.on('rejectReceiveVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('rejectReceiveVideo', user);
|
||||
});
|
||||
// 接听视频
|
||||
sock.on('answerVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('answerVideo', user);
|
||||
});
|
||||
// 挂断视频
|
||||
sock.on('hangupVideo', (user) => {
|
||||
httpIo.in(user.roomId).emit('hangupVideo', user);
|
||||
});
|
||||
// ======================================
|
||||
// addIceCandidate
|
||||
sock.on('addIceCandidate', (data) => {
|
||||
const toUser = rooms[data.user.roomId].find(item=>item.sockId!==data.user.sockId);
|
||||
console.log('addIceCandidate', toUser)
|
||||
socks[toUser.sockId].emit('addIceCandidate', data.candidate);
|
||||
});
|
||||
sock.on('receiveOffer', (data) => {
|
||||
const toUser = rooms[data.user.roomId].find(item=>item.sockId!==data.user.sockId);
|
||||
socks[toUser.sockId].emit('receiveOffer', data.offer);
|
||||
});
|
||||
sock.on('receiveAnswer', (data) => {
|
||||
const toUser = rooms[data.user.roomId].find(item=>item.sockId!==data.user.sockId);
|
||||
socks[toUser.sockId].emit('receiveAnswer', data.answer);
|
||||
});
|
||||
};
|
||||
httpIo.on('connection', httpConnectIoCallBack);
|
||||
|
||||
|
||||
|
523
webrtc-server/yarn.lock
Normal file
523
webrtc-server/yarn.lock
Normal file
@ -0,0 +1,523 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
accepts@^1.3.5, accepts@~1.3.4:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
||||
dependencies:
|
||||
mime-types "~2.1.24"
|
||||
negotiator "0.6.2"
|
||||
|
||||
after@0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.npm.taobao.org/after/download/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
|
||||
|
||||
any-promise@^1.1.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
|
||||
arraybuffer.slice@~0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.npm.taobao.org/arraybuffer.slice/download/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
|
||||
|
||||
async-limiter@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/async-limiter/download/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
|
||||
backo2@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npm.taobao.org/backo2/download/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||
|
||||
base64-arraybuffer@0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
|
||||
|
||||
base64-arraybuffer@0.1.5:
|
||||
version "0.1.5"
|
||||
resolved "https://registry.npm.taobao.org/base64-arraybuffer/download/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
|
||||
|
||||
base64id@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npm.taobao.org/base64id/download/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
|
||||
|
||||
better-assert@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npm.taobao.org/better-assert/download/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522"
|
||||
dependencies:
|
||||
callsite "1.0.0"
|
||||
|
||||
blob@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.npm.taobao.org/blob/download/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683"
|
||||
|
||||
cache-content-type@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/cache-content-type/download/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c"
|
||||
dependencies:
|
||||
mime-types "^2.1.18"
|
||||
ylru "^1.2.0"
|
||||
|
||||
callsite@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npm.taobao.org/callsite/download/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
|
||||
|
||||
co@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.npm.taobao.org/co/download/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
|
||||
|
||||
component-bind@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npm.taobao.org/component-bind/download/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
|
||||
|
||||
component-emitter@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
|
||||
|
||||
component-emitter@~1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npm.taobao.org/component-emitter/download/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
|
||||
component-inherit@0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.npm.taobao.org/component-inherit/download/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143"
|
||||
|
||||
content-disposition@~0.5.2:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
|
||||
dependencies:
|
||||
safe-buffer "5.1.2"
|
||||
|
||||
content-type@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
|
||||
cookie@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.npm.taobao.org/cookie/download/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
|
||||
|
||||
cookies@~0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npm.taobao.org/cookies/download/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90"
|
||||
dependencies:
|
||||
depd "~2.0.0"
|
||||
keygrip "~1.1.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.npm.taobao.org/debug/download/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.1.1:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npm.taobao.org/debug/download/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@~3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@~4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
deep-equal@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/deep-equal/download/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
|
||||
|
||||
delegates@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npm.taobao.org/delegates/download/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
|
||||
depd@^1.1.2, depd@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||
|
||||
depd@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npm.taobao.org/depd/download/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
|
||||
destroy@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
|
||||
encodeurl@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
|
||||
engine.io-client@~3.4.0:
|
||||
version "3.4.4"
|
||||
resolved "https://registry.npm.taobao.org/engine.io-client/download/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967"
|
||||
dependencies:
|
||||
component-emitter "~1.3.0"
|
||||
component-inherit "0.0.3"
|
||||
debug "~3.1.0"
|
||||
engine.io-parser "~2.2.0"
|
||||
has-cors "1.1.0"
|
||||
indexof "0.0.1"
|
||||
parseqs "0.0.6"
|
||||
parseuri "0.0.6"
|
||||
ws "~6.1.0"
|
||||
xmlhttprequest-ssl "~1.5.4"
|
||||
yeast "0.1.2"
|
||||
|
||||
engine.io-parser@~2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npm.taobao.org/engine.io-parser/download/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7"
|
||||
dependencies:
|
||||
after "0.8.2"
|
||||
arraybuffer.slice "~0.0.7"
|
||||
base64-arraybuffer "0.1.4"
|
||||
blob "0.0.5"
|
||||
has-binary2 "~1.0.2"
|
||||
|
||||
engine.io@~3.4.0:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.npm.taobao.org/engine.io/download/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c"
|
||||
dependencies:
|
||||
accepts "~1.3.4"
|
||||
base64id "2.0.0"
|
||||
cookie "0.3.1"
|
||||
debug "~4.1.0"
|
||||
engine.io-parser "~2.2.0"
|
||||
ws "^7.1.2"
|
||||
|
||||
escape-html@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
||||
fresh@~0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
|
||||
has-binary2@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npm.taobao.org/has-binary2/download/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
|
||||
dependencies:
|
||||
isarray "2.0.1"
|
||||
|
||||
has-cors@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npm.taobao.org/has-cors/download/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
|
||||
|
||||
http-assert@^1.3.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npm.taobao.org/http-assert/download/http-assert-1.4.1.tgz#c5f725d677aa7e873ef736199b89686cceb37878"
|
||||
dependencies:
|
||||
deep-equal "~1.0.1"
|
||||
http-errors "~1.7.2"
|
||||
|
||||
http-errors@^1.6.3, http-errors@^1.7.3:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.2.0"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
http-errors@~1.6.2:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.3"
|
||||
setprototypeof "1.1.0"
|
||||
statuses ">= 1.4.0 < 2"
|
||||
|
||||
http-errors@~1.7.2:
|
||||
version "1.7.3"
|
||||
resolved "https://registry.npm.taobao.org/http-errors/download/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||
dependencies:
|
||||
depd "~1.1.2"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.1.1"
|
||||
statuses ">= 1.5.0 < 2"
|
||||
toidentifier "1.0.0"
|
||||
|
||||
indexof@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.npm.taobao.org/indexof/download/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
|
||||
|
||||
inherits@2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
|
||||
inherits@2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
|
||||
is-generator-function@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.npm.taobao.org/is-generator-function/download/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522"
|
||||
|
||||
isarray@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npm.taobao.org/isarray/download/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
|
||||
|
||||
keygrip@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npm.taobao.org/keygrip/download/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226"
|
||||
dependencies:
|
||||
tsscmp "1.0.6"
|
||||
|
||||
koa-compose@^3.0.0:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npm.taobao.org/koa-compose/download/koa-compose-3.2.1.tgz#a85ccb40b7d986d8e5a345b3a1ace8eabcf54de7"
|
||||
dependencies:
|
||||
any-promise "^1.1.0"
|
||||
|
||||
koa-compose@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npm.taobao.org/koa-compose/download/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877"
|
||||
|
||||
koa-convert@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npm.taobao.org/koa-convert/download/koa-convert-1.2.0.tgz#da40875df49de0539098d1700b50820cebcd21d0"
|
||||
dependencies:
|
||||
co "^4.6.0"
|
||||
koa-compose "^3.0.0"
|
||||
|
||||
koa-send@^5.0.0, koa-send@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npm.taobao.org/koa-send/download/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79"
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
http-errors "^1.7.3"
|
||||
resolve-path "^1.4.0"
|
||||
|
||||
koa-static@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npm.taobao.org/koa-static/download/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943"
|
||||
dependencies:
|
||||
debug "^3.1.0"
|
||||
koa-send "^5.0.0"
|
||||
|
||||
koa@^2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.npm.taobao.org/koa/download/koa-2.13.0.tgz#25217e05efd3358a7e5ddec00f0a380c9b71b501"
|
||||
dependencies:
|
||||
accepts "^1.3.5"
|
||||
cache-content-type "^1.0.0"
|
||||
content-disposition "~0.5.2"
|
||||
content-type "^1.0.4"
|
||||
cookies "~0.8.0"
|
||||
debug "~3.1.0"
|
||||
delegates "^1.0.0"
|
||||
depd "^1.1.2"
|
||||
destroy "^1.0.4"
|
||||
encodeurl "^1.0.2"
|
||||
escape-html "^1.0.3"
|
||||
fresh "~0.5.2"
|
||||
http-assert "^1.3.0"
|
||||
http-errors "^1.6.3"
|
||||
is-generator-function "^1.0.7"
|
||||
koa-compose "^4.1.0"
|
||||
koa-convert "^1.2.0"
|
||||
on-finished "^2.3.0"
|
||||
only "~0.0.2"
|
||||
parseurl "^1.3.2"
|
||||
statuses "^1.5.0"
|
||||
type-is "^1.6.16"
|
||||
vary "^1.1.2"
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
|
||||
mime-db@1.44.0:
|
||||
version "1.44.0"
|
||||
resolved "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
|
||||
|
||||
mime-types@^2.1.18, mime-types@~2.1.24:
|
||||
version "2.1.27"
|
||||
resolved "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
|
||||
dependencies:
|
||||
mime-db "1.44.0"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
ms@2.1.2, ms@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
|
||||
negotiator@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
|
||||
object-component@0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.npm.taobao.org/object-component/download/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
|
||||
|
||||
on-finished@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
only@~0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.npm.taobao.org/only/download/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4"
|
||||
|
||||
parseqs@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.npm.taobao.org/parseqs/download/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
|
||||
dependencies:
|
||||
better-assert "~1.0.0"
|
||||
|
||||
parseqs@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.npm.taobao.org/parseqs/download/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5"
|
||||
|
||||
parseuri@0.0.5:
|
||||
version "0.0.5"
|
||||
resolved "https://registry.npm.taobao.org/parseuri/download/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a"
|
||||
dependencies:
|
||||
better-assert "~1.0.0"
|
||||
|
||||
parseuri@0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.npm.taobao.org/parseuri/download/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a"
|
||||
|
||||
parseurl@^1.3.2:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
|
||||
path-is-absolute@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
|
||||
resolve-path@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npm.taobao.org/resolve-path/download/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7"
|
||||
dependencies:
|
||||
http-errors "~1.6.2"
|
||||
path-is-absolute "1.0.1"
|
||||
|
||||
safe-buffer@5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
|
||||
setprototypeof@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
|
||||
|
||||
setprototypeof@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||
|
||||
socket.io-adapter@~1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npm.taobao.org/socket.io-adapter/download/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9"
|
||||
|
||||
socket.io-client@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npm.taobao.org/socket.io-client/download/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4"
|
||||
dependencies:
|
||||
backo2 "1.0.2"
|
||||
base64-arraybuffer "0.1.5"
|
||||
component-bind "1.0.0"
|
||||
component-emitter "1.2.1"
|
||||
debug "~4.1.0"
|
||||
engine.io-client "~3.4.0"
|
||||
has-binary2 "~1.0.2"
|
||||
has-cors "1.1.0"
|
||||
indexof "0.0.1"
|
||||
object-component "0.0.3"
|
||||
parseqs "0.0.5"
|
||||
parseuri "0.0.5"
|
||||
socket.io-parser "~3.3.0"
|
||||
to-array "0.1.4"
|
||||
|
||||
socket.io-parser@~3.3.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199"
|
||||
dependencies:
|
||||
component-emitter "~1.3.0"
|
||||
debug "~3.1.0"
|
||||
isarray "2.0.1"
|
||||
|
||||
socket.io-parser@~3.4.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.npm.taobao.org/socket.io-parser/download/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a"
|
||||
dependencies:
|
||||
component-emitter "1.2.1"
|
||||
debug "~4.1.0"
|
||||
isarray "2.0.1"
|
||||
|
||||
socket.io@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npm.taobao.org/socket.io/download/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb"
|
||||
dependencies:
|
||||
debug "~4.1.0"
|
||||
engine.io "~3.4.0"
|
||||
has-binary2 "~1.0.2"
|
||||
socket.io-adapter "~1.1.0"
|
||||
socket.io-client "2.3.0"
|
||||
socket.io-parser "~3.4.0"
|
||||
|
||||
"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
|
||||
to-array@0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.npm.taobao.org/to-array/download/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
|
||||
|
||||
toidentifier@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||
|
||||
tsscmp@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npm.taobao.org/tsscmp/download/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
|
||||
|
||||
type-is@^1.6.16:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
dependencies:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
vary@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
|
||||
ws@^7.1.2:
|
||||
version "7.3.1"
|
||||
resolved "https://registry.npm.taobao.org/ws/download/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
|
||||
|
||||
ws@~6.1.0:
|
||||
version "6.1.4"
|
||||
resolved "https://registry.npm.taobao.org/ws/download/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9"
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
xmlhttprequest-ssl@~1.5.4:
|
||||
version "1.5.5"
|
||||
resolved "https://registry.npm.taobao.org/xmlhttprequest-ssl/download/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
||||
|
||||
yeast@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npm.taobao.org/yeast/download/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
||||
|
||||
ylru@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npm.taobao.org/ylru/download/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"
|
23
webrtc-static/.eslintrc.js
Normal file
23
webrtc-static/.eslintrc.js
Normal file
@ -0,0 +1,23 @@
|
||||
// http://eslint.org/docs/user-guide/configuring
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
},
|
||||
globals: {
|
||||
Vue: true,
|
||||
VIDEO_VIEW: true,
|
||||
},
|
||||
env: {
|
||||
browser: true
|
||||
},
|
||||
extends: ['plugin:vue/essential', 'standard'],
|
||||
plugins: ['vue'],
|
||||
rules: {
|
||||
eqeqeq: 0,
|
||||
'comma-dangle': 0,
|
||||
'generator-star-spacing': 0,
|
||||
semi: [2, 'always'],
|
||||
'prefer-promise-reject-errors': 0
|
||||
}
|
||||
}
|
5
webrtc-static/babel.config.js
Normal file
5
webrtc-static/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
54
webrtc-static/package.json
Normal file
54
webrtc-static/package.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "webrtc-demo-client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "vue-cli-service serve",
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^2.6.5",
|
||||
"element-ui": "^2.13.2",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"vue": "^2.6.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.12.0",
|
||||
"@vue/cli-plugin-eslint": "^3.12.0",
|
||||
"@vue/cli-service": "^3.12.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
14
webrtc-static/public/index.html
Normal file
14
webrtc-static/public/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>webrtc-icon-192x192.png">
|
||||
<title>webrtc-demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="<%= BASE_URL %>video-view.js"></script>
|
||||
</body>
|
||||
</html>
|
2
webrtc-static/public/video-view.js
Normal file
2
webrtc-static/public/video-view.js
Normal file
File diff suppressed because one or more lines are too long
BIN
webrtc-static/public/webrtc-icon-192x192.png
Normal file
BIN
webrtc-static/public/webrtc-icon-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
25
webrtc-static/src/App.vue
Normal file
25
webrtc-static/src/App.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<Room/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Room from './pages/Room.vue';
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
Room
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
10
webrtc-static/src/main.js
Normal file
10
webrtc-static/src/main.js
Normal file
@ -0,0 +1,10 @@
|
||||
import Vue from 'vue';
|
||||
import ElementUI from 'element-ui';
|
||||
import 'element-ui/lib/theme-chalk/index.css';
|
||||
import App from './App.vue';
|
||||
Vue.use(ElementUI);
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
}).$mount('#app');
|
394
webrtc-static/src/pages/Room.vue
Normal file
394
webrtc-static/src/pages/Room.vue
Normal file
@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<div class="m-room-wrapper">
|
||||
<div class="can-support-rtc" v-if="canSupportVideo">
|
||||
<div class="form-area" v-if="showFormArea">
|
||||
<el-form
|
||||
:model="roomForm"
|
||||
:rules="rules"
|
||||
ref="roomForm"
|
||||
label-width="100px"
|
||||
class="room-form"
|
||||
>
|
||||
<el-form-item label="房间ID" prop="roomId">
|
||||
<el-input v-model.trim="roomForm.roomId" :disabled="!canClickBtn"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="userName">
|
||||
<el-input v-model.trim="roomForm.userName" :disabled="!canClickBtn"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm" :disabled="!canClickBtn">加入房间</el-button>
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="list-area" v-if="!showFormArea">
|
||||
<h2>当前房间id: {{ roomForm.roomId }} </h2>
|
||||
<h2>在线人数: {{ roomUsers.length }} </h2>
|
||||
<el-card class="box-card">
|
||||
<div v-for="item in roomUsers" :key="item.sockId" class="item">
|
||||
{{ item.userName }}
|
||||
</div>
|
||||
</el-card>
|
||||
<el-button type="primary" v-if="roomUsers.length > 1 && sockId" @click="toSendVideo">
|
||||
发起视频
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<h1>当前域名的浏览器不支持WebRTC!</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import socket from '../utils/socket.js';
|
||||
|
||||
export default {
|
||||
name: 'Room',
|
||||
created () {
|
||||
if (this.canSupportWebRTC()) {
|
||||
this.initSocketEvents();
|
||||
this.initVIDEO_VIEWSdk();
|
||||
}
|
||||
},
|
||||
data () {
|
||||
const validateRoomId = (rule, value, callback) => {
|
||||
const reg = /^\d{1,4}$/;
|
||||
if (!reg.test(value)) {
|
||||
return callback(new Error('房间ID只能为1-4位的数字'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const validateName = (rule, value, callback) => {
|
||||
const reg = /^[\u4e00-\u9fa5a-zA-Z-z]{1,10}$/;
|
||||
if (!reg.test(value)) {
|
||||
return callback(new Error('请输入合法的姓名'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
showFormArea: true,
|
||||
roomForm: {
|
||||
roomId: '',
|
||||
userName: ''
|
||||
},
|
||||
rules: {
|
||||
roomId: [
|
||||
{ required: true, message: '请输入房间ID', trigger: ['blur', 'change'] },
|
||||
{ validator: validateRoomId, trigger: ['blur', 'change'] }
|
||||
],
|
||||
userName: [
|
||||
{ required: true, message: '请输入姓名', trigger: ['blur', 'change'] },
|
||||
{ validator: validateName, trigger: ['blur', 'change'] }
|
||||
],
|
||||
},
|
||||
canClickBtn: true,
|
||||
sockId: '',
|
||||
roomUsers: [],
|
||||
canSupportVideo: false,
|
||||
localStream: null,
|
||||
peer: null,
|
||||
peerConfigs: {
|
||||
// 本地测试无需打洞 如部署到公网 需填写coturn的配置
|
||||
// iceServers: [{
|
||||
// urls: 'turn:xxx:3478',
|
||||
// credential: 'xxx',
|
||||
// username: 'xxx'
|
||||
// }],
|
||||
},
|
||||
offerOption: {
|
||||
offerToReceiveAudio: 1,
|
||||
offerToReceiveVideo: 1
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return Object.assign({}, { sockId: this.sockId }, this.roomForm);
|
||||
},
|
||||
receiveUser () {
|
||||
return this.roomUsers.find(item => item.sockId !== this.sockId);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
canSupportWebRTC () {
|
||||
// mediaDevices 只能在https下面才有
|
||||
// 该对象可提供对相机和麦克风等媒体输入设备的连接访问,也包括屏幕共享。
|
||||
if (typeof navigator.mediaDevices !== 'object') {
|
||||
this.$message.error('No navigator.mediaDevices');
|
||||
return false;
|
||||
}
|
||||
// 请求一个可用的媒体输入和输出设备的列表,例如麦克风,摄像机,耳机设备等
|
||||
if (typeof navigator.mediaDevices.enumerateDevices !== 'function') {
|
||||
this.$message.error('No navigator.mediaDevices.enumerateDevices');
|
||||
return false;
|
||||
}
|
||||
// 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。
|
||||
// 此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、
|
||||
// 一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D 转换器等等),也可能是其它轨道类型。
|
||||
if (typeof navigator.mediaDevices.getUserMedia !== 'function') {
|
||||
this.$message.error('No navigator.mediaDevices.getUserMedia');
|
||||
return false;
|
||||
}
|
||||
this.canSupportVideo = true;
|
||||
this.getDevices();
|
||||
return true;
|
||||
},
|
||||
async getDevices () {
|
||||
try {
|
||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||
VIDEO_VIEW.showDevicesNameByDevices(devices);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
const msg = `getDevices error: ${error.name} : ${error.message}`;
|
||||
this.$message.error(msg);
|
||||
}
|
||||
},
|
||||
initSocketEvents () {
|
||||
// 溜溜球 -0-
|
||||
window.onbeforeunload = () => {
|
||||
socket.emit('userLeave', {
|
||||
userName: this.roomForm.userName,
|
||||
sockId: this.sockId,
|
||||
roomId: this.roomForm.roomId,
|
||||
});
|
||||
};
|
||||
socket.on('connectionSuccess', (sockId) => {
|
||||
this.sockId = sockId;
|
||||
console.log('connectionSuccess client sockId:', sockId);
|
||||
});
|
||||
socket.on('checkRoomSuccess', (existRoomUsers) => {
|
||||
this.canClickBtn = true;
|
||||
if (existRoomUsers && existRoomUsers.length > 1) {
|
||||
this.$message.info('当前房间人数已满~请换个房间id');
|
||||
} else {
|
||||
this.showFormArea = false;
|
||||
this.roomUsers = [
|
||||
{
|
||||
userName: this.roomForm.userName + '(我)',
|
||||
sockId: this.sockId,
|
||||
roomId: this.roomForm.roomId,
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
socket.on('joinRoomSuccess', (roomUsers) => {
|
||||
console.log('joinRoomSuccess client user:', roomUsers);
|
||||
const otherUser = roomUsers.find(item => item.sockId !== this.sockId);
|
||||
if (!otherUser) return false;
|
||||
this.$message.success(`${otherUser.userName}加入了房间`);
|
||||
this.roomUsers = [otherUser, {
|
||||
userName: this.roomForm.userName + '(我)',
|
||||
sockId: this.sockId,
|
||||
roomId: this.roomForm.roomId,
|
||||
}];
|
||||
});
|
||||
socket.on('userLeave', (roomUsers) => {
|
||||
console.log('userLeave client user:', roomUsers);
|
||||
if (!roomUsers.length) {
|
||||
this.showFormArea = true;
|
||||
this.sockId = '';
|
||||
}
|
||||
const serverSockIdArr = roomUsers.map(item => item.sockId);
|
||||
this.roomUsers.forEach(item => {
|
||||
if (serverSockIdArr.indexOf(item.sockId) === -1) {
|
||||
this.$message.info(`${item.userName}离开了房间`);
|
||||
if (item.sockId === this.sockId) {
|
||||
this.showFormArea = true;
|
||||
this.sockId = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
this.roomUsers = roomUsers;
|
||||
this.roomUsers.forEach((item) => {
|
||||
if (item.sockId === this.sockId) {
|
||||
item.userName = item.userName + '(我)';
|
||||
}
|
||||
});
|
||||
// TODO: 挂断视频 0-0
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
});
|
||||
socket.on('disconnect', (message) => {
|
||||
this.showFormArea = true;
|
||||
this.sockId = '';
|
||||
console.log('client sock disconnect:', message);
|
||||
socket.emit('userLeave', this.user);
|
||||
// TODO: 挂断视频 0-0
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
});
|
||||
// ================== 视频相关 =====================
|
||||
|
||||
// 取消发送视频
|
||||
socket.on('cancelSendVideo', (user) => {
|
||||
const infoTips = user.sockId === this.sockId ? '您取消了发送视频' : '对方取消了发送视频';
|
||||
this.$message.info(infoTips);
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
});
|
||||
// 接收视频邀请
|
||||
socket.on('receiveVideo', (sender) => {
|
||||
if (this.user.sockId === sender.sockId) return false;
|
||||
VIDEO_VIEW.showReceiveVideoModalBySender(sender);
|
||||
});
|
||||
// 拒绝接收视频
|
||||
socket.on('rejectReceiveVideo', (user) => {
|
||||
const infoTips = user.sockId === this.sockId ? '您拒绝了接收视频' : '对方拒绝了接收视频';
|
||||
this.$message.info(infoTips);
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
});
|
||||
// 接听视频
|
||||
socket.on('answerVideo', async (user) => {
|
||||
VIDEO_VIEW.showInvideoModal();
|
||||
// 创建本地视频流信息
|
||||
this.localStream = await this.createLocalVideoStream();
|
||||
document.querySelector('#echat-local').srcObject = this.localStream;
|
||||
// Link: https://developer.mozilla.org/zh-CN/docs/Web/API/RTCPeerConnection
|
||||
// RTCPeerConnection 接口代表一个由本地计算机到远端的 WebRTC 连接
|
||||
// 呼叫方发送一个 offer(请求),被呼叫方发出一个 answer(应答)来回答请求
|
||||
this.peer = new RTCPeerConnection();
|
||||
console.log(this.peer);
|
||||
this.initPeerListen();
|
||||
// Adding a local stream won't trigger the onaddstream callback
|
||||
this.peer.addStream(this.localStream);
|
||||
if (user.sockId === this.sockId) {
|
||||
// 接收方
|
||||
} else {
|
||||
// 发送方 创建offer
|
||||
const offer = await this.peer.createOffer(this.offerOption);
|
||||
console.log('我的offer', offer);
|
||||
// send the offer to a server to be forwarded to the friend you're calling.
|
||||
await this.peer.setLocalDescription(offer);
|
||||
socket.emit('receiveOffer', { user: this.user, offer });
|
||||
}
|
||||
});
|
||||
// 挂断视频
|
||||
socket.on('hangupVideo', (user) => {
|
||||
const infoTips = user.sockId === this.sockId ? '您挂断了视频' : '对方挂断了视频';
|
||||
this.$message.info(infoTips);
|
||||
this.peer.close();
|
||||
this.peer = null;
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
document.querySelector('#echat-remote-1').srcObject = null;
|
||||
document.querySelector('#echat-local').srcObject = null;
|
||||
});
|
||||
//
|
||||
socket.on('addIceCandidate', async (candidate) => {
|
||||
await this.peer.addIceCandidate(candidate);
|
||||
});
|
||||
socket.on('receiveOffer', async (offer) => {
|
||||
// send the answer to a server to be forwarded back to the caller
|
||||
await this.peer.setRemoteDescription(offer);
|
||||
const answer = await this.peer.createAnswer();
|
||||
await this.peer.setLocalDescription(answer);
|
||||
socket.emit('receiveAnswer', { answer, user: this.user });
|
||||
});
|
||||
socket.on('receiveAnswer', (answer) => {
|
||||
// 处理应答, 同时在呼叫发起方,你会收到这个应答(前面被呼叫方发出的 answer),你需要将它设置为你的远端连接。
|
||||
this.peer.setRemoteDescription(answer);
|
||||
});
|
||||
},
|
||||
submitForm () {
|
||||
if (!this.sockId) {
|
||||
this.$message.error('socket未连接成功,请刷新再尝试!');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
this.$refs.roomForm.validate((valid) => {
|
||||
if (valid) {
|
||||
// 检查该房间人数
|
||||
this.canClickBtn = false;
|
||||
socket.emit('checkRoom', {
|
||||
roomId: this.roomForm.roomId,
|
||||
sockId: this.sockId,
|
||||
userName: this.roomForm.userName
|
||||
});
|
||||
} else {
|
||||
console.log('error submit!!');
|
||||
}
|
||||
});
|
||||
},
|
||||
resetForm () {
|
||||
this.$refs.roomForm.resetFields();
|
||||
this.roomForm.roomId = '';
|
||||
this.roomForm.userName = '';
|
||||
},
|
||||
// 发送视频
|
||||
toSendVideo () {
|
||||
socket.emit('toSendVideo', this.user);
|
||||
VIDEO_VIEW.showStartVideoModalByReceiver(this.receiveUser);
|
||||
},
|
||||
initVIDEO_VIEWSdk () {
|
||||
const configOptios = {
|
||||
startVideoCancelCb: this.startVideoCancelCb,
|
||||
receiveVideoCancelCb: this.receiveVideoCancelCb,
|
||||
receiveVideoAnswerCb: this.receiveVideoAnswerCb,
|
||||
hangUpVideoCb: this.hangUpVideoCb,
|
||||
openMikeCb: this.openMikeCb,
|
||||
closeMikeCb: this.closeMikeCb,
|
||||
openCammerCb: this.openCammerCb,
|
||||
closeCammerCb: this.closeCammerCb,
|
||||
toScreenCb: this.toScreenCb,
|
||||
};
|
||||
VIDEO_VIEW.configCallBack(configOptios);
|
||||
},
|
||||
startVideoCancelCb () {
|
||||
socket.emit('cancelSendVideo', this.user);
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
},
|
||||
receiveVideoCancelCb () {
|
||||
socket.emit('rejectReceiveVideo', this.user);
|
||||
VIDEO_VIEW.hideAllVideoModal();
|
||||
},
|
||||
receiveVideoAnswerCb () {
|
||||
socket.emit('answerVideo', this.user);
|
||||
},
|
||||
hangUpVideoCb () {
|
||||
socket.emit('hangupVideo', this.user);
|
||||
},
|
||||
openMikeCb () {
|
||||
|
||||
},
|
||||
closeMikeCb () {
|
||||
|
||||
},
|
||||
openCammerCb () {
|
||||
|
||||
},
|
||||
closeCammerCb () {
|
||||
|
||||
},
|
||||
toScreenCb () {
|
||||
|
||||
},
|
||||
async createLocalVideoStream () {
|
||||
const constraints = { audio: true, video: true };
|
||||
// Link: https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
|
||||
const localStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
console.log('localStream:', localStream);
|
||||
return localStream;
|
||||
},
|
||||
initPeerListen () {
|
||||
this.peer.onicecandidate = (event) => {
|
||||
if (event.candidate) { socket.emit('addIceCandidate', { candidate: event.candidate, user: this.user }); }
|
||||
};
|
||||
this.peer.onaddstream = (event) => {
|
||||
// 拿到对方的视频流
|
||||
document.querySelector('#echat-remote-1').srcObject = event.stream;
|
||||
};
|
||||
this.peer.onclose = () => {
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.m-room-wrapper{
|
||||
margin-top: 20px;
|
||||
}
|
||||
.m-room-wrapper .box-card {
|
||||
width: 480px;
|
||||
}
|
||||
.m-room-wrapper .box-card .item{
|
||||
padding: 18px 0;
|
||||
}
|
||||
</style>
|
4
webrtc-static/src/utils/socket.js
Normal file
4
webrtc-static/src/utils/socket.js
Normal file
@ -0,0 +1,4 @@
|
||||
import io from 'socket.io-client';
|
||||
const host = 'localhost:3000';
|
||||
const socket = io.connect(host);
|
||||
export default socket;
|
3
webrtc-static/vue.config.js
Normal file
3
webrtc-static/vue.config.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
outputDir: '../webrtc-server/static'
|
||||
};
|
7578
webrtc-static/yarn.lock
Normal file
7578
webrtc-static/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user