diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 47e882a..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "env": {
- "browser": true,
- "es2020": true
- },
- "rules": {
- "jsdoc/newline-after-description": "off"
- },
- "parserOptions": {
- "ecmaVersion": 2020
- },
- "extends": "videojs"
-}
\ No newline at end of file
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..8c3d0f0
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,60 @@
+name: Test
+
+on:
+ push:
+ branches: [ main, dev ]
+ pull_request:
+ branches: [ main, dev ]
+
+jobs:
+ test:
+ name: Test (${{ matrix.os }} - ${{ matrix.browser }})
+ continue-on-error: true
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-latest, windows-latest, macos-latest]
+ browser: [chrome, firefox, edge, safari]
+ include:
+ - os: ubuntu-latest
+ browser: chrome
+ - os: ubuntu-latest
+ browser: firefox
+ - os: windows-latest
+ browser: chrome
+ - os: windows-latest
+ browser: edge
+ - os: macos-latest
+ browser: safari
+ exclude:
+ - os: ubuntu-latest
+ browser: edge
+ - os: ubuntu-latest
+ browser: safari
+ - os: windows-latest
+ browser: firefox
+ - os: windows-latest
+ browser: safari
+ - os: macos-latest
+ browser: chrome
+ - os: macos-latest
+ browser: firefox
+ - os: macos-latest
+ browser: edge
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Run tests
+ run: npm run test:${{ matrix.browser }}
+ env:
+ TEST_HEADLESS: ${{ matrix.browser != 'safari' }}
diff --git a/.gitignore b/.gitignore
index fbd0e60..7e7ec0d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,10 @@ docs/api/
test/dist/
.eslintcache
.yo-rc.json
+
+# VitePress
+docs/.vitepress/cache/
+docs/.vitepress/dist/
+docs/.vitepress/temp/
+docs/public/*.bundle.js
+__screenshots__
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
new file mode 100644
index 0000000..b680eb7
--- /dev/null
+++ b/docs/.vitepress/config.ts
@@ -0,0 +1,80 @@
+import { defineConfig, Plugin } from 'vitepress';
+import copyDistPlugin from './plugins/copydist';
+import { resolve } from 'path';
+import { fileURLToPath } from 'url';
+import container from 'markdown-it-container';
+import monaco from 'vite-plugin-monaco-editor';
+
+function renderExample(tokens: any[], idx: number) {
+ const token = tokens[idx]
+ if (token.nesting === 1) {
+ return ``;
+ }
+
+ return '';
+}
+
+
+export default defineConfig({
+ title: 'Video.js Plugin',
+ description: 'Videojs set of plugins for playing streams from the Ceeblue cloud',
+ head: [
+ ['link', { rel: 'icon', href: '/favicon.ico' }],
+ ['meta', { name: 'theme-color', content: '#646cff' }]
+ ],
+ themeConfig: {
+ logo: {
+ src: '/logo.svg',
+ alt: 'Ceeblue Logo'
+ },
+ nav: [
+ { text: 'Getting Started', link: '/' },
+ { text: 'Examples', link: '/examples/' },
+ { text: 'Demo', link: 'https://ceeblue-demo-mirror.pages.dev/' },
+ { text: 'API', link: 'https://docs.ceeblue.net/reference/welcome-to-the-ceeblue-streaming-cloud-api' }
+ ],
+ sidebar: [
+ {
+ text: 'Guide',
+ items: [
+ { text: 'Getting Started', link: '/' }
+ ]
+ },
+ {
+ text: 'Examples',
+ items: [
+ { text: 'Simple player', link: '/examples/simple' },
+ ]
+ },
+ {
+ text: 'API',
+ items: [
+ { text: 'API Reference', link: 'https://docs.ceeblue.net/reference/welcome-to-the-ceeblue-streaming-cloud-api' }
+ ]
+ }
+ ],
+ socialLinks: [
+ { icon: 'github', link: 'https://github.com/ceebluetv/videojs-plugins' }
+ ]
+ },
+ vite: {
+ plugins: [
+ copyDistPlugin(resolve(fileURLToPath(import.meta.url), '..', '..', '..')),
+ // @ts-expect-error - monaco has bad exports
+ monaco.default({
+ languageWorkers: ['typescript', 'css', 'html', 'json'],
+ customDistPath() {
+ return resolve(fileURLToPath(import.meta.url), '..', '..', 'public');
+ }
+ }),
+ ],
+ ssr: {
+ noExternal: ['monaco-editor']
+ }
+ },
+ markdown: {
+ config: (md) => {
+ md.use(container, 'example', { render: renderExample })
+ }
+ }
+})
diff --git a/docs/.vitepress/plugins/copydist.ts b/docs/.vitepress/plugins/copydist.ts
new file mode 100644
index 0000000..ba03672
--- /dev/null
+++ b/docs/.vitepress/plugins/copydist.ts
@@ -0,0 +1,27 @@
+import { existsSync, mkdirSync, readdirSync, copyFileSync } from 'fs'
+import { resolve, join } from 'path'
+
+/**
+ * A simple plugin to copy the dist folder to the public folder.
+ * Because vitepress doesn't support multiple public folders (with vite-multiple-assets plugin).
+ */
+export default function copyDistPlugin(base: string) {
+ return {
+ name: 'copy-dist',
+ configureServer() {
+ const distPath = resolve(base, 'dist')
+ const publicPath = resolve(base, 'docs', 'public', 'dist')
+
+ if (!existsSync(publicPath)) {
+ mkdirSync(publicPath, { recursive: true })
+ }
+
+ readdirSync(distPath).forEach(file => {
+ copyFileSync(
+ join(distPath, file),
+ join(publicPath, file)
+ )
+ })
+ }
+ }
+}
diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css
new file mode 100644
index 0000000..958c80e
--- /dev/null
+++ b/docs/.vitepress/theme/custom.css
@@ -0,0 +1,19 @@
+:root {
+ --vp-c-brand: #646cff;
+ --vp-c-brand-light: #747bff;
+ --vp-c-brand-lighter: #9499ff;
+ --vp-c-brand-dark: #535bf2;
+ --vp-c-brand-darker: #454ce1;
+}
+
+/* Logo styling */
+.VPNavBarTitle .logo {
+ height: 32px;
+ margin-right: 8px;
+}
+
+.VPNavBarTitle {
+ display: flex;
+ align-items: center;
+}
+
diff --git a/docs/.vitepress/theme/exampleEditorViewer.ts b/docs/.vitepress/theme/exampleEditorViewer.ts
new file mode 100644
index 0000000..a6649eb
--- /dev/null
+++ b/docs/.vitepress/theme/exampleEditorViewer.ts
@@ -0,0 +1,82 @@
+import * as monaco from 'monaco-editor'
+
+class CeebluePluginExample extends HTMLElement {
+ #editor: monaco.editor.IStandaloneCodeEditor | undefined = undefined;
+ #wrapper: HTMLElement | undefined = undefined;
+ #observer: MutationObserver | undefined = undefined;
+
+ connectedCallback () {
+ const codeEl = this.querySelector('pre code');
+ const initial = (codeEl
+ ? codeEl.textContent
+ : this.textContent?.trim()) || '';
+
+ this.#wrapper = document.createElement('div')
+ Object.assign(this.#wrapper.style, {
+ display: 'grid',
+ gap: '1rem',
+ gridTemplateColumns: '1fr',
+ fontFamily: 'system-ui, sans-serif',
+ marginTop: '1rem'
+ })
+
+ const style = document.createElement('style');
+ style.textContent = `
+ @media (max-width: 768px) {
+ .ceeblue-plugin-example-wrapper {
+ grid-template-columns: 1fr;
+ }
+ }
+ `
+ document.head.appendChild(style)
+ this.#wrapper.classList.add('ceeblue-plugin-example-wrapper')
+
+ const frame = Object.assign(document.createElement('iframe'), {
+ style: 'width:100%;aspect-ratio:16/9.2;border:1px solid #ccc;border-radius:6px;'
+ })
+ const editorBox = Object.assign(document.createElement('div'), {
+ style: 'height:400px;border:1px solid #ccc;border-radius:6px;'
+ })
+ this.#wrapper.append(frame, editorBox)
+
+ this.insertAdjacentElement('afterend', this.#wrapper)
+ this.style.display = 'none';
+
+ // Initialize editor with theme based on current document class
+ const isDark = document.documentElement.classList.contains('dark')
+ this.#editor = monaco.editor.create(editorBox, {
+ value: initial,
+ language: 'html',
+ theme: isDark ? 'vs-dark' : 'vs',
+ automaticLayout: true
+ })
+
+ // VitePress theme change observer
+ this.#observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
+ const isDark = document.documentElement.classList.contains('dark')
+ monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs')
+ }
+ })
+ })
+
+ this.#observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ['class']
+ })
+
+ const render = () => (frame.srcdoc = this.#editor?.getValue() || '')
+ render()
+ this.#editor.onDidChangeModelContent(render)
+ }
+
+ disconnectedCallback () {
+ this.#observer?.disconnect()
+ this.#editor?.dispose()
+ this.#wrapper?.remove()
+ }
+}
+
+customElements.define('ceeblue-plugin-example', CeebluePluginExample)
+export default CeebluePluginExample
\ No newline at end of file
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
new file mode 100644
index 0000000..3c84a7f
--- /dev/null
+++ b/docs/.vitepress/theme/index.ts
@@ -0,0 +1,9 @@
+import DefaultTheme from 'vitepress/theme';
+import './custom.css';
+
+export default {
+ ...DefaultTheme,
+ enhanceApp({ app }) {
+
+ }
+}
diff --git a/docs/examples/index.md b/docs/examples/index.md
new file mode 100644
index 0000000..2d1ec49
--- /dev/null
+++ b/docs/examples/index.md
@@ -0,0 +1,22 @@
+# Examples
+
+This section provides practical examples of how to use Ceeblue Video.js Plugin in different scenarios.
+
+## Available Examples
+
+- [Simple player](/examples/simple) - Simple setup with default configuration
+
+## Running Examples Locally
+
+To run these examples locally:
+
+1. Clone the repository
+2. Install dependencies:
+ ```bash
+ npm install
+ ```
+3. Start the development server:
+ ```bash
+ npm run docs:dev
+ ```
+4. Navigate to the examples section in the documentation
diff --git a/docs/examples/simple.md b/docs/examples/simple.md
new file mode 100644
index 0000000..186c121
--- /dev/null
+++ b/docs/examples/simple.md
@@ -0,0 +1,87 @@
+
+
+# Basic Setup
+
+This example demonstrates the basic setup of Video.js with the Ceeblue plugin.
+
+## HTML Setup
+
+
+:::example
+
+```html
+
+
+
+
+
+ Ceeblue Video.js Plugin
+
+
+
+
+
+
+
+
+```
+:::
+
+## Best Practices
+
+1. Always include error handling.
+2. Use appropriate video dimensions.
+3. Consider mobile responsiveness.
+4. Test across different browsers.
+5. Monitor player events for debugging.
+
+## Next Steps
+
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..f0cc749
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,41 @@
+# Ceeblue Video.js Plugin
+
+An open-source plugin for Video.js that allows you to play streams from the Ceeblue cloud, Supports WebRTS, WHIP, WebRTC.
+
+## Features
+
+- Ceeblue's WebRTS support.
+- WebRTC, WHEP support.
+- Playing streams from the Ceeblue cloud.
+- DRM support.
+- Video quality switching.
+- Protocol fallback.
+- Real-time universal stats API.
+
+## Quick Start
+
+::: code-group
+
+```sh [npm]
+npm install @ceeblue/videojs-plugins
+```
+
+```html [jsdelivr]
+
+```
+
+```sh [yarn]
+yarn add @ceeblue/videojs-plugins
+```
+
+```sh [pnpm]
+pnpm add @ceeblue/videojs-plugins
+```
+
+:::
+
+## Documentation
+
+- [Examples](/examples/)
+- [Demo](https://ceeblue-demo-mirror.pages.dev/)
+- [API Reference](https://docs.ceeblue.net/reference/welcome-to-the-ceeblue-streaming-cloud-api)
diff --git a/docs/public/logo.svg b/docs/public/logo.svg
new file mode 100644
index 0000000..58707e9
--- /dev/null
+++ b/docs/public/logo.svg
@@ -0,0 +1,42 @@
+
+
diff --git a/eslint.config.ts b/eslint.config.ts
new file mode 100644
index 0000000..11a93f6
--- /dev/null
+++ b/eslint.config.ts
@@ -0,0 +1,188 @@
+import pluginHtml from '@html-eslint/eslint-plugin';
+import htmlParser from '@html-eslint/parser';
+import typescript from '@typescript-eslint/eslint-plugin';
+import parserTypeScript from '@typescript-eslint/parser';
+import markdown from 'eslint-plugin-markdown';
+import jsonLight from 'eslint-plugin-json-light';
+import jsdoc from 'eslint-plugin-jsdoc';
+import globals from 'globals';
+
+// For of https://www.npmjs.com/package/eslint-config-videojs
+// But with typescript and html support.
+// And flat eslint config.
+export default [
+ {
+ name: 'ignore-dist',
+ ignores: [
+ '**/dist/**',
+ '**/node_modules/**',
+ '**/docs/public/dist/**',
+ '**/docs/.vitepress/**',
+ '**/public/**'
+ ]
+ },
+ {
+ name: 'ceeblue-videojs-plugin',
+ files: ['src/**/*.ts'],
+ languageOptions: {
+ parser: parserTypeScript,
+ parserOptions: {
+ ecmaVersion: 2023,
+ sourceType: 'module',
+ node: true
+ },
+ globals: {
+ ...globals.browser,
+ ...globals.node,
+ ...globals.es2023,
+ RTCIceServer: false,
+ MediaProvider: false
+ }
+ },
+ plugins: {
+ typescript,
+ markdown,
+ 'json-light': jsonLight,
+ jsdoc
+ },
+ rules: {
+ 'array-bracket-newline': ['error', 'consistent'],
+ 'block-scoped-var': 'off',
+ 'brace-style': ['error', '1tbs', {allowSingleLine: false}],
+ 'camelcase': ['error', {properties: 'always'}],
+ 'comma-dangle': ['error', 'never'],
+ 'comma-spacing': ['error', {before: false, after: true}],
+ 'comma-style': ['error', 'last'],
+ 'complexity': 'off',
+ 'consistent-return': 'off',
+ 'consistent-this': 'warn',
+ 'curly': ['error', 'all'],
+ 'default-case': 'off',
+ 'dot-notation': 'error',
+ 'eol-last': 'error',
+ 'eqeqeq': ['error', 'allow-null'],
+ 'func-names': 'off',
+ 'function-paren-newline': ['error', 'multiline'],
+ 'guard-for-in': 'off',
+ 'handle-callback-err': ['error', '^(err|error|anySpecificError)$'],
+ 'indent': ['error', 2],
+ 'key-spacing': ['error', {beforeColon: false, afterColon: true}],
+ 'keyword-spacing': 'error',
+ 'max-depth': 'off',
+ 'max-len': 'off',
+ 'max-nested-callbacks': ['warn', 4],
+ 'max-params': 'off',
+ 'max-statements': 'off',
+ 'new-cap': ['error', {newIsCap: true, capIsNew: false}],
+ 'new-parens': 'error',
+ 'newline-after-var': ['error', 'always'],
+ 'no-alert': 'error',
+ 'no-array-constructor': 'error',
+ 'no-bitwise': 'off',
+ 'no-caller': 'error',
+ 'no-catch-shadow': 'error',
+ 'no-cond-assign': ['error', 'except-parens'],
+ 'no-console': 'error',
+ 'no-const-assign': 'error',
+ 'no-constant-condition': 'off',
+ 'no-continue': 'off',
+ 'no-control-regex': 'error',
+ 'no-debugger': 'error',
+ 'no-delete-var': 'error',
+ 'no-div-regex': 'off',
+ 'no-dupe-args': 'error',
+ 'no-dupe-keys': 'error',
+ 'no-duplicate-case': 'error',
+ 'no-else-return': 'error',
+ 'no-empty': 'error',
+ 'no-empty-character-class': 'error',
+ 'no-eq-null': 'error',
+ 'no-eval': 'error',
+ 'no-ex-assign': 'error',
+ 'no-extend-native': 'error',
+ 'no-extra-bind': 'error',
+ 'no-extra-boolean-cast': 'error',
+ 'no-extra-parens': 'off',
+ 'no-extra-semi': 'error',
+ 'no-fallthrough': 'error',
+ 'no-floating-decimal': 'error',
+ 'no-func-assign': 'error',
+ 'no-implied-eval': 'error',
+ 'no-inline-comments': 'error',
+ 'no-inner-declarations': ['error', 'functions'],
+ 'no-invalid-regexp': 'error',
+ 'no-irregular-whitespace': 'error',
+ 'no-iterator': 'error',
+ 'no-label-var': 'error',
+ 'no-labels': 'error',
+ 'no-lone-blocks': 'error',
+ 'no-lonely-if': 'error',
+ 'no-loop-func': 'warn',
+ 'no-mixed-requires': ['off', false],
+ 'no-mixed-spaces-and-tabs': ['error', false],
+ 'no-multi-spaces': 'error',
+ 'no-multi-str': 'error',
+ 'no-multiple-empty-lines': ['error', {max: 1}],
+ 'no-native-reassign': 'error',
+ 'no-negated-in-lhs': 'error',
+ 'no-nested-ternary': 'warn',
+ 'no-new': 'error',
+ 'no-new-func': 'error',
+ 'no-new-object': 'error',
+ 'no-new-require': 'error',
+ 'no-new-wrappers': 'error',
+ 'no-obj-calls': 'error',
+ 'no-octal': 'error',
+ 'no-octal-escape': 'error',
+ 'no-path-concat': 'error',
+ 'no-param-reassign': 'off',
+ 'no-plusplus': 'off',
+ 'no-process-env': 'off',
+ 'no-process-exit': 'off',
+ 'no-proto': 'error',
+ 'no-redeclare': 'error',
+ 'no-regex-spaces': 'error',
+ 'no-restricted-modules': 'off',
+ 'no-return-assign': 'error',
+ 'no-script-url': 'error',
+ 'no-self-compare': 'warn',
+ 'no-sequences': 'error',
+ 'no-shadow': 'error',
+ 'no-shadow-restricted-names': 'error',
+ 'no-spaced-func': 'error',
+ 'no-sparse-arrays': 'error',
+ 'no-sync': 'off',
+ 'no-ternary': 'off',
+ 'no-throw-literal': 'error',
+ 'no-trailing-spaces': 'error',
+ 'no-undef': 'error',
+ 'no-undef-init': 'error',
+ 'no-undefined': 'off',
+ 'no-underscore-dangle': 'off',
+ 'no-unreachable': 'error',
+ 'no-unused-expressions': 'error',
+ 'no-unused-vars': ['error', {vars: 'all', args: 'none'}],
+ 'no-use-before-define': ['error', {functions: false}],
+ 'no-var': 'error',
+ 'no-void': 'off',
+ 'no-warning-comments': ['warn', {terms: ['todo', 'fixme', 'xxx'], location: 'start'}],
+ 'no-with': 'error',
+ 'object-curly-newline': ['error', {consistent: true}],
+ 'jsdoc/newline-after-description': 'off'
+ }
+ },
+ {
+ name: 'html-examples',
+ ...pluginHtml.configs["flat/recommended"],
+ files: ["examples/**/*.html"],
+ plugins: {
+ '@html-eslint': pluginHtml
+ },
+ languageOptions: {
+ parser: htmlParser
+ },
+ rules: {
+ "@html-eslint/indent": ["error", 2]
+ }
+ }
+];
diff --git a/examples/js/Player.js b/examples/js/Player.js
deleted file mode 100644
index 76cfcb8..0000000
--- a/examples/js/Player.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/* eslint-disable no-undef */
-
-const WebRTCSourceHandler = videojs.getTech('Html5').sourceHandlers.find((value) => value.name === 'ceeblue/videojs-plugins');
-const EventEmitter = WebRTCSourceHandler.webrtcClient.utils.EventEmitter;
-const SourceController = videojs.getPlugin('SourceController');
-const SourceType = SourceController.SourceType;
-
-/**
- * Player class sample
- *
- * Allows to create your videojs player in few lines with all the `SourceController` features.
- *
- * Features of Player:
- * - Use default configuration of Ceeblue protocols (WebRTC, LLHLS, DASH, HLS)
- * - Instantiate the videojs player (avoid conflicts of usage)
- * - Has custom `retryCount` option to retry multiple time the list of sources
- * - Show modal dialog when all sources have failed (`options.modal = false` to disable)
- *
- * Usage :
- *
- * ```javascript
- * var player = new Player('video-player');
- * player.start({endPoint:'', streamName: ''});
- * ```
- */
-export class Player extends EventEmitter {
- /**
- * Event triggered when the source changes.
- *
- * @param {string|null} source the source to play or null if no more source is available
- */
- onSourceChanged(source) {}
-
- /**
- * Set the current source dynamically
- *
- * @param {string} source the source type to play
- */
- set source(source) {
- this._sourceController.source = source;
- }
-
- /**
- * Get the videojs player instance
- */
- get player() {
- return this._player;
- }
-
- /**
- * Create a Player instance.
- *
- * The options can contain the following additional properties:
- * - retryCount: the number of times to retry to play the sources if all sources failed. Default: 0
- * - qualityButton: True to enable the quality button. Default: true
- * - modal: True to enable the modal dialogs. Default: true
- * - auto: True to automatically switch to the next source if the current one fails. Default: true
- *
- * @param {string|HTMLVideoElement} idOrTag The id of the videojs player or the video element or the videojs player instance
- * @param {ConnectParams} connectParams the connection parameters
- * @param {Array?} sources an array of sources to try in order
- * @param {Object} options The videojs options for the player
- */
- constructor(idOrTag, connectParams, sources = [SourceType.WEBRTC, SourceType.LLHLS, SourceType.DASH, SourceType.HLS], options = {}) {
- super();
- if (typeof idOrTag === 'string' || idOrTag instanceof HTMLVideoElement) {
- this._player = videojs(idOrTag, options);
- } else {
- throw new Error('The first argument must be a string or an HTMLVideoElement');
- }
- this._modal = options.modal !== false;
- this._totalRetryCount = options.retryCount || 0;
- this._retryCount = 0;
-
- this._sourceController = new SourceController(this._player, {...connectParams}, [...sources]);
- this._sourceController.auto = options.auto !== false;
- this._sourceController.onSourceChanged = (source) => {
- if (!source && options.auto) {
- if (this._retryCount < this._totalRetryCount) {
- this._retryCount++;
- videojs.log('Retry to play the sources', this._retryCount, '/', this._totalRetryCount);
- setTimeout(() => {
- this._sourceController.start();
- }, 0);
- return;
- }
-
- videojs.log('Playback stopped, all sources failed');
- if (this._modal) {
- const ModalDialog = videojs.getComponent('ModalDialog');
- const errorModal = new ModalDialog(this._player, {
- content: 'Playback stopped, all sources failed',
- temporary: true,
- pauseOnOpen: false
- });
-
- this._player.addChild(errorModal);
- errorModal.open();
- }
- }
- this.onSourceChanged(source);
- };
- }
-
- /**
- * Start the Player with the given SourceType
- *
- * @param {String?} sourceType the name of the selected source to start with, if null the first source is played
- */
- start(sourceType) {
- if (this._sourceController.started) {
- return;
- }
-
- this._retryCount = 0;
- this._sourceController.start(sourceType);
- }
-
- /**
- * Stop the Player
- */
- stop() {
- this._sourceController.stop();
- }
-
- /**
- * Dispose the Player
- */
- dispose() {
- this._sourceController.stop();
- this._player.dispose();
- }
-}
diff --git a/examples/player.html b/examples/player.html
index 5c6ef1e..2b83a6f 100644
--- a/examples/player.html
+++ b/examples/player.html
@@ -4,25 +4,25 @@
-->
-
- Ceeblue Videojs Player
-
-
-
+
+ Ceeblue Videojs Player
+
+
+
-
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
-
-

-
-
-

-
-
-
-
-
-
-
-
-
+
+
+
+
+

+
+
+
-