├── .gitignore ├── demo └── index.html ├── .github └── workflows │ └── publish.yml ├── webpack.config.js ├── package.json ├── README.md └── src └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WHEP Video Component Demo 7 | 12 | 13 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16 16 | registry-url: https://registry.npmjs.org/ 17 | - run: | 18 | npm ci 19 | npm run build 20 | npm publish --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 23 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 3 | const package = require('./package.json'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = { 7 | mode: 'production', 8 | entry: './src/index.js', 9 | output: { 10 | library: 'whepvideo.component', 11 | libraryExport: 'default', 12 | libraryTarget: 'umd', 13 | filename: 'whep-video.component.js', 14 | path: path.resolve(__dirname, 'dist'), 15 | }, 16 | plugins: [ 17 | new HtmlWebpackPlugin({ 18 | title: package.version, 19 | template: './demo/index.html', 20 | inject: 'body', 21 | scriptLoading: 'blocking' 22 | }), 23 | // disable dynamic imports, it doesn't work with the umd output 24 | new webpack.optimize.LimitChunkCountPlugin({ 25 | maxChunks: 1, 26 | }), 27 | ], 28 | devtool: 'source-map', 29 | devServer: { 30 | static: path.resolve(__dirname, 'dist'), 31 | compress: true, 32 | port: 1337, 33 | host: '0.0.0.0', 34 | }, 35 | stats: { 36 | warningsFilter: [/Failed to parse source map/], 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@eyevinn/whep-video-component", 3 | "version": "0.1.0", 4 | "description": "WHEP video web-component", 5 | "main": "dist/whep-video.component.js", 6 | "scripts": { 7 | "build": "rm -rf dist/ && webpack --config webpack.config.js", 8 | "dev": "webpack-dev-server --hot", 9 | "postversion": "git push && git push --tags", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "files": [ 13 | "dist" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/Eyevinn/whep-video-component.git" 18 | }, 19 | "keywords": [ 20 | "WHEP" 21 | ], 22 | "author": "Eyevinn Technology AB ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/Eyevinn/whep-video-component/issues" 26 | }, 27 | "homepage": "https://github.com/Eyevinn/whep-video-component#readme", 28 | "dependencies": { 29 | "@eyevinn/webrtc-player": "^0.12.0" 30 | }, 31 | "devDependencies": { 32 | "html-webpack-plugin": "^5.5.0", 33 | "webpack": "^5.74.0", 34 | "webpack-cli": "^4.10.0", 35 | "webpack-dev-server": "^4.11.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `` 2 | 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Slack](http://slack.streamingtech.se/badge.svg)](http://slack.streamingtech.se) 4 | 5 | A web component for [WHEP](https://eyevinntechnology.medium.com/standardized-webrtc-based-broadcast-streaming-is-being-recognized-by-the-industry-fbc24df54cf4) WebRTC video playback. 6 | 7 | ## Example 8 | 9 | ```html 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ``` 24 | 25 | # Support 26 | 27 | Join our [community on Slack](http://slack.streamingtech.se) where you can post any questions regarding any of our open source projects. Eyevinn's consulting business can also offer you: 28 | 29 | - Further development of this component 30 | - Customization and integration of this component into your platform 31 | - Support and maintenance agreement 32 | 33 | Contact [sales@eyevinn.se](mailto:sales@eyevinn.se) if you are interested. 34 | 35 | # About Eyevinn Technology 36 | 37 | Eyevinn Technology is an independent consultant firm specialized in video and streaming. Independent in a way that we are not commercially tied to any platform or technology vendor. 38 | 39 | At Eyevinn, every software developer consultant has a dedicated budget reserved for open source development and contribution to the open source community. This give us room for innovation, team building and personal competence development. And also gives us as a company a way to contribute back to the open source community. 40 | 41 | Want to know more about Eyevinn and how it is to work here. Contact us at work@eyevinn.se! 42 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { WebRTCPlayer } from "@eyevinn/webrtc-player"; 2 | 3 | const ComponentAttribute = { 4 | DYNAMIC: { 5 | SOURCE: 'src', 6 | AUTOPLAY: 'autoplay', 7 | MUTED: 'muted', 8 | } 9 | } 10 | 11 | const isSet = (value) => value === "" || !!value; 12 | 13 | export default class WhepVideoComponent extends HTMLElement { 14 | static get observedAttributes() { 15 | return Object.values(ComponentAttribute.DYNAMIC) 16 | } 17 | 18 | constructor() { 19 | super(); 20 | const wrapper = this.setupDOM(); 21 | this.setupPlayer(wrapper); 22 | } 23 | 24 | setupDOM() { 25 | this.attachShadow({ mode: 'open' }); 26 | const { shadowRoot } = this; 27 | 28 | let styleTag = document.createElement('style'); 29 | styleTag.innerHTML = "video { width: inherit; } div { width: inherit }"; 30 | shadowRoot.appendChild(styleTag); 31 | 32 | const wrapper = document.createElement('div'); 33 | shadowRoot.appendChild(wrapper); 34 | this.video = document.createElement('video'); 35 | wrapper.appendChild(this.video); 36 | 37 | return wrapper; 38 | } 39 | 40 | setupPlayer(wrapper) { 41 | this.player = new WebRTCPlayer({ 42 | video: this.video, 43 | type: "whep" 44 | }); 45 | } 46 | 47 | async attributeChangedCallback(name) { 48 | const src = this.getAttribute(ComponentAttribute.DYNAMIC.SOURCE); 49 | const autoplay = this.getAttribute(ComponentAttribute.DYNAMIC.AUTOPLAY); 50 | const muted = this.getAttribute(ComponentAttribute.DYNAMIC.MUTED); 51 | 52 | if (name === ComponentAttribute.DYNAMIC.SOURCE) { 53 | if (isSet(src)) { 54 | await this.player.load(new URL(src)); 55 | if (isSet(autoplay)) { 56 | this.video.muted = isSet(muted); 57 | this.video.autoplay = true; 58 | } 59 | } else { 60 | console.error("Missing src attribute in element"); 61 | } 62 | } 63 | if (name === ComponentAttribute.DYNAMIC.MUTED) { 64 | this.video.muted = isSet(muted); 65 | } 66 | } 67 | 68 | disconnectedCallback() { 69 | this.player.destroy(); 70 | } 71 | } 72 | 73 | customElements.define('whep-video', WhepVideoComponent); --------------------------------------------------------------------------------