screen-share
Mustafa Yontar 4 years ago
parent ae544add07
commit 3c87a375c3
  1. 183
      package-lock.json
  2. 15
      package.json
  3. 2
      public/index.html
  4. 71
      src/App.vue
  5. 1
      src/assets/logo.svg
  6. 197
      src/components/HelloWorld.vue
  7. 124
      src/lib/RemoteFeed.vue
  8. 279
      src/lib/VueJanus.vue
  9. 2664
      src/lib/adapter.js
  10. 3456
      src/lib/janus.js
  11. 10
      src/main.js
  12. 62
      src/plugins/axios.js
  13. 21
      src/plugins/janus.js
  14. 23
      src/plugins/vuetify.js
  15. 5
      vue.config.js

183
package-lock.json generated

@ -2083,6 +2083,48 @@
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
"dev": true
},
"axios": {
"version": "0.18.1",
"resolved": "https://registry.npm.taobao.org/axios/download/axios-0.18.1.tgz",
"integrity": "sha1-/z8N4ue10YDnV62YAA8Qgbh7zqM=",
"dev": true,
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-3.1.0.tgz",
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz",
"integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
},
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npm.taobao.org/is-buffer/download/is-buffer-2.0.4.tgz",
"integrity": "sha1-PlcvI8hBGlz9lVfISeNmXgspBiM=",
"dev": true
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"babel-eslint": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
@ -2954,6 +2996,17 @@
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
"clone-deep": {
"version": "4.0.1",
"resolved": "https://registry.npm.taobao.org/clone-deep/download/clone-deep-4.0.1.tgz",
"integrity": "sha1-wZ/Zvbv4WUK0/ZechNz31fB8I4c=",
"dev": true,
"requires": {
"is-plain-object": "^2.0.4",
"kind-of": "^6.0.2",
"shallow-clone": "^3.0.0"
}
},
"coa": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
@ -6467,6 +6520,12 @@
}
}
},
"interpret": {
"version": "1.2.0",
"resolved": "https://registry.npm.taobao.org/interpret/download/interpret-1.2.0.tgz",
"integrity": "sha1-1QYaYiS+WOgIOYX1AU2EQ1lXYpY=",
"dev": true
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@ -6845,6 +6904,11 @@
}
}
},
"jquery": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
"integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
},
"js-message": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
@ -9188,6 +9252,15 @@
"readable-stream": "^2.0.2"
}
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npm.taobao.org/rechoir/download/rechoir-0.6.2.tgz",
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
"dev": true,
"requires": {
"resolve": "^1.1.6"
}
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
@ -9513,6 +9586,14 @@
"inherits": "^2.0.1"
}
},
"rtcpeerconnection-shim": {
"version": "1.2.15",
"resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
"integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==",
"requires": {
"sdp": "^2.6.0"
}
},
"run-async": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
@ -9561,6 +9642,36 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"dev": true
},
"sass": {
"version": "1.26.3",
"resolved": "https://registry.npm.taobao.org/sass/download/sass-1.26.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass%2Fdownload%2Fsass-1.26.3.tgz",
"integrity": "sha1-QS31RIYUO3a1plzfdWnob0Rln0Y=",
"dev": true,
"requires": {
"chokidar": ">=2.0.0 <4.0.0"
}
},
"sass-loader": {
"version": "8.0.2",
"resolved": "https://registry.npm.taobao.org/sass-loader/download/sass-loader-8.0.2.tgz?cache=0&sync_timestamp=1578921506275&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsass-loader%2Fdownload%2Fsass-loader-8.0.2.tgz",
"integrity": "sha1-3r7NjDziQ8dkVPLoKQSCFQOACQ0=",
"dev": true,
"requires": {
"clone-deep": "^4.0.1",
"loader-utils": "^1.2.3",
"neo-async": "^2.6.1",
"schema-utils": "^2.6.1",
"semver": "^6.3.0"
},
"dependencies": {
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-6.3.0.tgz?cache=0&sync_timestamp=1581458146669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-6.3.0.tgz",
"integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=",
"dev": true
}
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
@ -9577,6 +9688,11 @@
"ajv-keywords": "^3.4.1"
}
},
"sdp": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
"integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@ -9775,6 +9891,15 @@
"safe-buffer": "^5.0.1"
}
},
"shallow-clone": {
"version": "3.0.1",
"resolved": "https://registry.npm.taobao.org/shallow-clone/download/shallow-clone-3.0.1.tgz",
"integrity": "sha1-jymBrZJTH1UDWwH7IwdppA4C76M=",
"dev": true,
"requires": {
"kind-of": "^6.0.2"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -9796,6 +9921,17 @@
"integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
"dev": true
},
"shelljs": {
"version": "0.8.3",
"resolved": "https://registry.npm.taobao.org/shelljs/download/shelljs-0.8.3.tgz",
"integrity": "sha1-p/MxlSDr8J7oEnWyNorbKGZZsJc=",
"dev": true,
"requires": {
"glob": "^7.0.0",
"interpret": "^1.0.0",
"rechoir": "^0.6.2"
}
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
@ -11075,6 +11211,30 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
"integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
},
"vue-cli-plugin-axios": {
"version": "0.0.4",
"resolved": "https://registry.npm.taobao.org/vue-cli-plugin-axios/download/vue-cli-plugin-axios-0.0.4.tgz",
"integrity": "sha1-KdTrSCdcf+FbkuH9XZX74qlmQ28=",
"dev": true
},
"vue-cli-plugin-vuetify": {
"version": "2.0.5",
"resolved": "https://registry.npm.taobao.org/vue-cli-plugin-vuetify/download/vue-cli-plugin-vuetify-2.0.5.tgz",
"integrity": "sha1-Cbp4koZT/H44Aeo35RIPlXQT3aQ=",
"dev": true,
"requires": {
"semver": "^7.1.2",
"shelljs": "^0.8.3"
},
"dependencies": {
"semver": {
"version": "7.1.3",
"resolved": "https://registry.npm.taobao.org/semver/download/semver-7.1.3.tgz?cache=0&sync_timestamp=1581458146669&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsemver%2Fdownload%2Fsemver-7.1.3.tgz",
"integrity": "sha1-5DRc5zBxxT8zZEXPwZ77HDEd8qY=",
"dev": true
}
}
},
"vue-eslint-parser": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.0.0.tgz",
@ -11162,6 +11322,20 @@
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
"dev": true
},
"vuetify": {
"version": "2.2.20",
"resolved": "https://registry.npm.taobao.org/vuetify/download/vuetify-2.2.20.tgz",
"integrity": "sha1-jLBC9ydPORMPjUDYXSPWLRwzyuQ="
},
"vuetify-loader": {
"version": "1.4.3",
"resolved": "https://registry.npm.taobao.org/vuetify-loader/download/vuetify-loader-1.4.3.tgz",
"integrity": "sha1-3xMjxVi+CYkId+X76BezpxpsU40=",
"dev": true,
"requires": {
"loader-utils": "^1.2.0"
}
},
"watchpack": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
@ -11516,6 +11690,15 @@
}
}
},
"webrtc-adapter": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.5.1.tgz",
"integrity": "sha512-R5LkIR/APjODkstSXFOztOmINXQ0nqIGfUoKTtCzjyiDXHNgwhkqZ9vi8UzGyjfUBibuZ0ZzVyV10qtuLGW3CQ==",
"requires": {
"rtcpeerconnection-shim": "^1.2.15",
"sdp": "^2.12.0"
}
},
"websocket-driver": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz",

@ -3,22 +3,31 @@
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"serve": "vue-cli-service serve --port 8091",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.4",
"vue": "^2.6.11"
"jquery": "^3.4.1",
"vue": "^2.6.11",
"vuetify": "^2.2.11",
"webrtc-adapter": "^7.5.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.2.0",
"@vue/cli-plugin-eslint": "~4.2.0",
"@vue/cli-service": "~4.2.0",
"axios": "^0.18.0",
"babel-eslint": "^10.0.3",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.1.2",
"vue-template-compiler": "^2.6.11"
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"vue-cli-plugin-axios": "0.0.4",
"vue-cli-plugin-vuetify": "~2.0.5",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.3.0"
},
"eslintConfig": {
"root": true,

@ -6,6 +6,8 @@
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
</head>
<body>
<noscript>

@ -1,28 +1,63 @@
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
<v-app>
<v-app-bar
app
color="primary"
dark
>
<div class="d-flex align-center">
<v-img
alt="Vuetify Logo"
class="shrink mr-2"
contain
src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
transition="scale-transition"
width="40"
/>
<v-img
alt="Vuetify Name"
class="shrink mt-1 hidden-sm-and-down"
contain
min-width="100"
src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
width="100"
/>
</div>
<v-spacer></v-spacer>
<v-btn
href="https://github.com/vuetifyjs/vuetify/releases/latest"
target="_blank"
text
>
<span class="mr-2">Latest Release</span>
<v-icon>mdi-open-in-new</v-icon>
</v-btn>
</v-app-bar>
<v-content>
<VueJanus server="https://vid.w3ic.org/janus" :room="room" username="vuettest"/>
</v-content>
</v-app>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import VueJanus from "@/lib/VueJanus";
export default {
name: 'App',
components: {
HelloWorld
VueJanus
},
data: () => ({
test: ' ',
room:1234
}),
mounted () {
}
}
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

After

Width:  |  Height:  |  Size: 539 B

@ -1,58 +1,151 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
<v-container>
<v-row class="text-center">
<v-col cols="12">
<v-img
:src="require('../assets/logo.svg')"
class="my-3"
contain
height="200"
/>
</v-col>
<v-col class="mb-4">
<h1 class="display-2 font-weight-bold mb-3">
Welcome to Vuetify
</h1>
<p class="subheading font-weight-regular">
For help and collaboration with other Vuetify developers,
<br>please join our online
<a
href="https://community.vuetifyjs.com"
target="_blank"
>Discord Community</a>
</p>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
What's next?
</h2>
<v-row justify="center">
<a
v-for="(next, i) in whatsNext"
:key="i"
:href="next.href"
class="subheading mx-3"
target="_blank"
>
{{ next.text }}
</a>
</v-row>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
Important Links
</h2>
<v-row justify="center">
<a
v-for="(link, i) in importantLinks"
:key="i"
:href="link.href"
class="subheading mx-3"
target="_blank"
>
{{ link.text }}
</a>
</v-row>
</v-col>
<v-col
class="mb-5"
cols="12"
>
<h2 class="headline font-weight-bold mb-3">
Ecosystem
</h2>
<v-row justify="center">
<a
v-for="(eco, i) in ecosystem"
:key="i"
:href="eco.href"
class="subheading mx-3"
target="_blank"
>
{{ eco.text }}
</a>
</v-row>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
export default {
name: 'HelloWorld',
data: () => ({
ecosystem: [
{
text: 'vuetify-loader',
href: 'https://github.com/vuetifyjs/vuetify-loader',
},
{
text: 'github',
href: 'https://github.com/vuetifyjs/vuetify',
},
{
text: 'awesome-vuetify',
href: 'https://github.com/vuetifyjs/awesome-vuetify',
},
],
importantLinks: [
{
text: 'Documentation',
href: 'https://vuetifyjs.com',
},
{
text: 'Chat',
href: 'https://community.vuetifyjs.com',
},
{
text: 'Made with Vuetify',
href: 'https://madewithvuejs.com/vuetify',
},
{
text: 'Twitter',
href: 'https://twitter.com/vuetifyjs',
},
{
text: 'Articles',
href: 'https://medium.com/vuetify',
},
],
whatsNext: [
{
text: 'Explore components',
href: 'https://vuetifyjs.com/components/api-explorer',
},
{
text: 'Select a layout',
href: 'https://vuetifyjs.com/layout/pre-defined',
},
{
text: 'Frequently Asked Questions',
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions',
},
],
}),
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

@ -0,0 +1,124 @@
<template>
<div class="vid_main">
<video ref="feed_video" width="100%" height="100%" autoplay playsinline/>
<div class="info">{{ bitrate }}</div>
<div class="actions"></div>
</div>
</template>
<script>
export default {
props: {
janusInit: Object,
opaqueId: String,
room: Number,
mypvtid: String,
feedid: String
},
name: "RemoteFeed",
data: () => ({
remoteFeed: null,
bitrateTimer: null,
bitrate: 0
}),
mounted () {
console.log(this.janusInit)
this.janusInit.attach({
plugin: "janus.plugin.videoroom",
opaqueId: this.opaqueId,
success: this.onSuccess,
onmessage: this.onMessage,
error: function(error) {
console.log(" -- Error attaching plugin...", error);
// bootbox.alert("Error attaching plugin... " + error);
},
webrtcState: function(on) {
console.log("Janus says this WebRTC PeerConnection " + (on ? "up" : "down") + " now");
},
onlocalstream: this.onlocalstream,
onremotestream: this.onStream
})
},
methods: {
onSuccess (pluginHandle) {
this.remoteFeed = pluginHandle;
this.remoteFeed.simulcastStarted = false;
console.log('Remote feed success')
var subscribe = { "request": "join", "room": this.room, "ptype": "subscriber", "feed": this.feedid, "private_id": this.mypvtid };
this.remoteFeed.send({"message": subscribe});
},
onlocalstream (locals) {
console.log(locals)
},
onStream (stream) {
console.log('stream is strarted')
this.$refs.feed_video.srcObject = stream
setInterval(() => {
this.bitrate = this.remoteFeed.getBitrate()
},1000)
},
feedResponse (jsep) {
var body = { "request": "start", "room": this.room }
this.remoteFeed.send({"message": body, "jsep": jsep})
},
feedError (error) {
console.log("WebRTC error:", error)
},
onMessage (msg, jsep) {
console.log(msg)
console.log(jsep)
console.log("-------------------------")
var event = msg["videoroom"];
if(msg["error"] !== undefined && msg["error"] !== null) {
console.log(msg["error"]);
} else if(event !== undefined && event != null) {
this.remoteFeed.rfid = msg["id"]
this.remoteFeed.rfdisplay = msg["display"]
} else if(event === "event") {
var substream = msg["substream"]
var temporal = msg["temporal"]
console.log(substream,temporal)
}
if(jsep !== undefined && jsep !== null) {
console.log('jsep')
console.log(jsep)
this.remoteFeed.createAnswer(
{
jsep: jsep,
// Add data:true here if you want to subscribe to datachannels as well
// (obviously only works if the publisher offered them in the first place)
media: { audioSend: false, videoSend: false }, // We want recvonly audio/video
success: this.feedResponse,
error: this.feedError
});
}
},
onError (error) {
console.log(error)
}
}
}
</script>
<style scoped>
.actions {
position: absolute;
bottom:0;
left:0;
}
.vid_main {
position: relative;
}
.info {
position: absolute;
right: 0;
top:0;
}
</style>

@ -0,0 +1,279 @@
<template>
<v-container>
<v-dialog
v-model="deviceSelectDialog"
max-width="290"
>
<v-card>
<v-card-title class="headline">Configuration</v-card-title>
<v-card-text>
<v-row>
<v-col md="12" cols="12">
<v-select
label="Please select video device"
v-model="selectedVideo"
:items="videoDevices"
item-value="deviceId"
item-text="label"
></v-select>
</v-col>
<v-col md="12" cols="12">
<v-select
label="Please select audio device"
:items="audioDevices"
v-model="selectedAudio"
item-value="deviceId"
item-text="label"
></v-select>
</v-col>
</v-row>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="green darken-1"
text
@click="deviceSelectDialog = false"
>
Disagree
</v-btn>
<v-btn
color="green darken-1"
text
@click="selectDevice"
>
Agree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-btn @click="genRemoteFeed">Remote Feed</v-btn>
<v-row>
<v-col cols="12" md="3">
<video ref="ownstream" class="rounded centered" id="myvideo" width="100%" height="100%" autoplay
playsinline muted="muted"/>
</v-col>
<v-col cols="12" md="3" v-for="remoteStream in remoteStreams" :key="remoteStream.id">
<RemoteFeed :opaqueId="opaqueId" :mypvtid="mypvtid" :feedid="remoteStream.id" :janusInit="janusInit" :room="room" ></RemoteFeed>
</v-col>
</v-row>
</v-container>
</template>
<script>
import RemoteFeed from "@/lib/RemoteFeed";
export default {
components: {RemoteFeed},
props: {
server: String,
room: Number,
username: String
},
data() {
return {
janusInit: null,
opaqueId: null,
pluginHandle: null,
mystream: null,
myid: '',
mypvtid: '',
selectedVideo: null,
selectedAudio: null,
videoDevices: [],
audioDevices: [],
deviceSelectDialog: false,
remoteStreams: []
}
},
name: "VueJanus",
mounted() {
this.janusInit = new this.$janus({
success: this.janusSuccess,
server: this.server,
error: function (error) {
this.$janus.error(error);
},
destroyed: function () {
window.location.reload();
}
})
},
methods: {
janusPluginError: function (error) {
console.log(error)
},
consentDialog: function (on) {
// this.$janus.debug("Consent dialog should be " + (on ? "on" : "off") + " now");
console.log(on)
},
janusPluginSuccess: function (pluginHandle) {
this.pluginHandle = pluginHandle
this.$janus.log("Plugin attached! (" + this.pluginHandle.getPlugin() + ", id=" + this.pluginHandle.getId() + ")");
this.$janus.log(" -- This is a publisher/manager");
this.$janus.listDevices(this.initDevices);
},
initDevices: function (devices) {
console.log(devices)
devices.forEach(device => {
if (device.kind === 'videoinput') {
this.videoDevices.push(device)
}
if (device.kind === 'audioinput') {
this.audioDevices.push(device)
}
})
this.deviceSelectDialog = true
},
genRemoteFeed: function () {
this.remoteStreams.forEach(item => {
this.createRemoteFeed(item)
})
},
offerSend: function (jsep) {
console.log('vid offer')
var publish = {"request": "configure", "audio": true, "video": true};
this.pluginHandle.send({"message": publish, "jsep": jsep});
},
controlPublishers: function(msg) {
msg["publishers"].forEach(item => {
var has = false
this.remoteStreams.forEach(rm => {
if (item.id === rm.id) has = true
})
if (!has) {
this.remoteStreams.push(item)
}
})
},
janusMessage: function (msg, jsep) {
// this.$janus.debug(" ::: Got a message (publisher) :::");
console.log(msg);
// this.$janus.debug(jsep);
var event = msg["videoroom"];
// this.$janus.debug("Event: " + event);
if (event !== undefined) {
if (event === "joined") {
this.myid = msg["id"];
this.mypvtid = msg["private_id"];
this.$janus.log("Successfully joined room " + msg["room"] + " with ID " + this.myid);
this.restartDevices()
if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
this.controlPublishers(msg)
}
} else if(event === "event") {
// Any new feed to attach to?
if (msg["publishers"] !== undefined && msg["publishers"] !== null) {
this.controlPublishers(msg)
} else if(msg["leaving"] !== undefined && msg["leaving"] !== null) {
console.log("leaving")
this.remoteStreams.forEach(item => {
if (item.id === msg['leaving']) this.remoteStreams.splice(this.remoteStreams.indexOf(item),1)
})
}
}
}
if (jsep !== undefined && jsep !== null) {
console.log("Handling SDP as well...");
console.log(jsep);
this.pluginHandle.handleRemoteJsep({jsep: jsep});
var audio = msg["audio_codec"];
if (this.mystream && this.mystream.getAudioTracks() && this.mystream.getAudioTracks().length > 0 && !audio) {
// Audio has been rejected
//toastr.warning("Our audio stream has been rejected, viewers won't hear us");
console.log("Our audio stream has been rejected, viewers won't hear us");
}
}
},
onlocalstream: function (stream) {
// this.$janus.debug(" ::: Got a local stream :::");
// this.$janus.debug(stream);
this.mystream = stream
console.log(this.$refs)
console.log(this.$refs["ownstream"])
this.$refs.ownstream.srcObject = this.mystream
},
restartDevices: function () {
this.deviceSelectDialog = false
var media = {
audioRecv: false, videoRecv: false, audioSend: true, videoSend: true,
audio: {
deviceId: {
exact: this.selectedAudio
}
},
replaceAudio: true, // This is only needed in case of a renegotiation
video: {
deviceId: {
exact: this.selectedVideo
}
},
replaceVideo: true,
};
this.pluginHandle.createOffer(
{
media: media,
success: this.offerSend,
error: function (error) {
// An error has occurred ...
console.log(error)
}
})
},
webrtcState: function (on) {
this.$janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
},
mediaState: function (medium, on) {
this.$janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium);
},
selectDevice: function () {
console.log(this.selectedVideo)
console.log(this.selectedAudio)
// this.$refs.ownstream.setSinkId(this.selectedVideo)
// this.$refs.ownstream.setSinkId(this.selectedAudio)
var register = {"request": "join", "room": this.room, "ptype": "publisher", "display": this.username};
this.pluginHandle.send({"message": register});
},
janusSuccess: function () {
this.opaqueId = "videoroomtest-" + this.$janus.randomString(12);
this.janusInit.attach(
{
plugin: "janus.plugin.videoroom",
opaqueId: this.opaqueId,
success: this.janusPluginSuccess,
consentDialog: this.consentDialog,
error: this.janusPluginError,
mediaState: this.mediaState,
webrtcState: this.webrtcState,
onmessage: this.janusMessage,
onlocalstream: this.onlocalstream
}
)
}
}
}
</script>
<style scoped>
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,8 +1,14 @@
import Vue from 'vue'
import './plugins/axios'
import JanusPlugin from "@/plugins/janus"
import App from './App.vue'
import vuetify from './plugins/vuetify';
Vue.config.productionTip = false
Vue.config.productionTip = false
console.log("janus is installed", Vue.$janus)
new Vue({
render: h => h(App),
vuetify,
JanusPlugin,
render: h => h(App)
}).$mount('#app')

@ -0,0 +1,62 @@
"use strict";
import Vue from 'vue';
import axios from "axios";
// Full config: https://github.com/axios/axios#request-config
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
let config = {
// baseURL: process.env.baseURL || process.env.apiUrl || ""
// timeout: 60 * 1000, // Timeout
// withCredentials: true, // Check cross-site Access-Control
};
const _axios = axios.create(config);
_axios.interceptors.request.use(
function(config) {
// Do something before request is sent
return config;
},
function(error) {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
_axios.interceptors.response.use(
function(response) {
// Do something with response data
return response;
},
function(error) {
// Do something with response error
return Promise.reject(error);
}
);
// eslint-disable-next-line no-unused-vars
Plugin.install = function(Vue, options) {
Vue.axios = _axios;
window.axios = _axios;
Object.defineProperties(Vue.prototype, {
axios: {
get() {
return _axios;
}
},
$axios: {
get() {
return _axios;
}
},
});
};
Vue.use(Plugin)
export default Plugin;

@ -0,0 +1,21 @@
"use strict";
import Vue from 'vue'
import Janus from "@/lib/janus"
const _janus = Janus
const JanusPlugin = {
// eslint-disable-next-line no-unused-vars
install(Vue, options) {
console.log('install call')
console.log(_janus)
_janus.init({debug: true, callback: function() {
Vue.prototype.$janus = _janus
}})
}
}
export default JanusPlugin;
Vue.use(JanusPlugin)

@ -0,0 +1,23 @@
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
theme: {
options: {
customProperties: true,
},
themes: {
light: {
primary: '#ee44aa',
secondary: '#424242',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107'
},
},
},
});

@ -0,0 +1,5 @@
module.exports = {
"transpileDependencies": [
"vuetify"
]
}