From 8909a5e81edb026bbff6fe362242d886ca3aa5e5 Mon Sep 17 00:00:00 2001 From: gemstone Date: Mon, 15 May 2023 15:19:14 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20add=20fixtures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + fixtures/flv/index.js | 322 ++++++++++++++++++++++++++++ fixtures/hls/index.js | 398 +++++++++++++++++++++++++++++++++++ fixtures/mp4/index.js | 25 +++ fixtures/music/index.js | 84 ++++++++ fixtures/xgplayer/index.html | 94 +++++++++ fixtures/xgplayer/index.js | 283 +++++++++++++++++++++++++ 7 files changed, 1207 insertions(+) create mode 100644 fixtures/flv/index.js create mode 100644 fixtures/hls/index.js create mode 100644 fixtures/mp4/index.js create mode 100644 fixtures/music/index.js create mode 100644 fixtures/xgplayer/index.html create mode 100644 fixtures/xgplayer/index.js diff --git a/.gitignore b/.gitignore index 29cb891a..4567ea34 100755 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,7 @@ packages/*/types/ .DS_Store fixtures/*/* !fixtures/*/index.html +!fixtures/*/index.js #http-server cert *.pem diff --git a/fixtures/flv/index.js b/fixtures/flv/index.js new file mode 100644 index 00000000..8cc0a4b3 --- /dev/null +++ b/fixtures/flv/index.js @@ -0,0 +1,322 @@ +import Player from '../../packages/xgplayer/src' +import FlvPlayer from '../../packages/xgplayer-flv/src' + +localStorage.setItem('xgd', 1) +function defaultOpt() { + return { + isLive: true, + autoplay: false, + autoplayMuted: false, + retryTimes: 3, + retryCount: 3, + retryDelay: 1000, + analyzeDuration: 5000, + loadTimeout: 10000, + bufferBehind: 10, + maxJumpDistance: 3, + maxReaderInterval: 5000, + seamlesslyReload: false + } +} +var cachedOpt = localStorage.getItem('xg:test:flv:opt') +try { cachedOpt = JSON.parse(cachedOpt) } catch (error) { cachedOpt = undefined } +var opts = Object.assign({ + // url: 'https://1011.hlsplay.aodianyun.com/demo/game.flv', + url: 'https://pull-flv-l1.douyincdn.com/stage/stream-399911386870710302_ld.flv?keeptime=00093a80&wsSecret=84c8c84e064fb6c6aaad6ec54c5c8247&wsTime=63315a10&abr_pts=1950715', +}, defaultOpt(), cachedOpt) +var testPoint = Number(localStorage.getItem('xg:test:flv:point')) + +if (isNaN(testPoint)) testPoint = 0 + +window.onload = function () { + var dTestPoint = document.getElementById('test-point') + var dTestPointDesc = document.getElementById('test-point-desc') + + var doPresetUrl = document.getElementById('preset-url') + var doUrl = document.getElementById('url') + var doAutoplay = document.getElementById('autoplay') + var doAutoplayMuted = document.getElementById('autoplay-muted') + var doSeamlesslyReload = document.getElementById('seamlessly-reload') + var doLoadTimeout = document.getElementById('load-timeout') + var doRetryCount = document.getElementById('retry-count') + var doRetryDelay = document.getElementById('retry-delay') + var doAnalyzeDuration = document.getElementById('analyze-duration') + var doFetchOptions = document.getElementById('fetch-options') + var doBufferBehind = document.getElementById('buffer-behind') + var doMaxJumpDistance = document.getElementById('max-jump-distance') + var doMaxReaderInterval = document.getElementById('max-reader-interval') + var doTargetLatency = document.getElementById('target-latency') + var doMaxLatency = document.getElementById('max-latency') + var doDisconnectTime = document.getElementById('disconnect-time') + + var dbResetOpt = document.getElementById('reset-opt') + var dbApplyOpt = document.getElementById('apply-opt') + var dbPlay = document.getElementById('play') + var dbPause = document.getElementById('pause') + var dbReplay = document.getElementById('replay') + var dbDestroy = document.getElementById('destroy') + var dbSwitchUrl = document.getElementById('switch-url') + var dbSetUrl = document.getElementById('set-url') + var dbSeek = document.getElementById('seek') + + var dlEvent = document.getElementById('event') + var dlError = document.getElementById('error') + var dlLogPause = document.getElementById('log-pause') + + var dsSpeed = document.getElementById('speed') + var dsFrame = document.getElementById('frame') + var dsBuffer = document.getElementById('buffer') + var dsOption = document.getElementById('option') + + dTestPoint.selectedIndex = testPoint + dsOption.innerHTML = '
' + JSON.stringify(opts, null, 2) + '
' + + function inp(d) { return d.getElementsByTagName('input')[0] } + + var player + + function updateOpts(key, value, type) { + if (type === 'object') { + try { + value = (new Function('x', 'return ' + value))() + } catch (error) { } + } + + if(type === 'number') { + opts[key] = Number(value) + }else{ + opts[key] = value + } + + + localStorage.setItem('xg:test:flv:opt', JSON.stringify(opts)) + dsOption.innerHTML = '
' + JSON.stringify(opts, null, 2) + '
' + } + function resetOpts() { + opts = Object.assign({ url: opts.url }, defaultOpt()) + localStorage.setItem('xg:test:flv:opt', JSON.stringify(opts)) + window.location.reload() + } + function initPlayer() { + if (player) { + player.destroy() + setTimeout(init, 100) + } else { + init() + } + function init() { + window.player = player = new Player({ + el: document.getElementById('player'), + plugins: [FlvPlayer], + url: opts.url, + isLive: opts.isLive, + autoplay: opts.autoplay, + autoplayMuted: opts.autoplayMuted, + flv: opts + }); + dlEvent.innerHTML = '' + dlError.innerHTML = '' + + function pushEvent(name, value, container) { + container = container || dlEvent + if (container === dlEvent && dlLogPause.checked) return + console.debug('[test]', name, value) + if (container === dlEvent && logFilter && !logFilter(name, value)) { + return + } + try { + value = JSON.stringify(value) + } catch (error) { + } + var record = document.createElement('div') + record.innerHTML = '
' + name + ' / ' + player.video.currentTime + '' + value + '
' + container.prepend(record) + } + + player.on('loadstart', function (event) { pushEvent('loadstart', event) }) + player.on('loadeddata', function (event) { pushEvent('loadeddata', event) }) + player.on('play', function (event) { pushEvent('play', event) }) + player.on('pause', function (event) { pushEvent('pause', event) }) + player.on('ended', function (event) { pushEvent('ended', event) }) + player.on('autoplay_was_prevented', function (event) { pushEvent('autoplay_was_prevented', event) }) + player.on('playing', function (event) { pushEvent('playing', event) }) + player.on('seeking', function (event) { pushEvent('seeking', event) }) + player.on('seeked', function (event) { pushEvent('seeked', event) }) + player.on('waiting', function (event) { pushEvent('waiting', event) }) + player.on('canplay', function (event) { pushEvent('canplay', event) }) + player.on('durationchange', function (event) { pushEvent('durationchange', event) }) + player.on('ready', function (event) { pushEvent('ready', event) }) + player.on('complete', function (event) { pushEvent('complete', event) }) + player.on('urlchange', function (event) { pushEvent('urlchange', event) }) + player.on('destroy', function (event) { pushEvent('destroy', event) }) + player.on('replay', function (event) { pushEvent('replay', event) }) + player.on('retry', function (event) { pushEvent('retry', event) }) + player.on('core_event', function (event) { pushEvent(event.eventName, event) }) + player.on('error', function (event) { pushEvent(event.errorType, event, dlError) }) + } + } + + function highlight (d) {d.classList.add('bg-yellow-300')} + function show(d, h) { d.style.display = 'block'; if (h) highlight(d) } + + var logFilter + var desc = '' + switch (testPoint) { + case 0: { + show(doAutoplay) + show(doAutoplayMuted) + show(doSeamlesslyReload) + show(doLoadTimeout) + show(doRetryCount) + show(doRetryDelay) + show(doAnalyzeDuration) + show(doFetchOptions) + show(doBufferBehind) + show(doMaxJumpDistance) + show(doTargetLatency) + show(doMaxLatency) + show(doDisconnectTime) + show(doMaxReaderInterval) + } + break; + case 1: { + desc = '设置高亮四个参数。
查看"开发者工具"的Network面板是否进行相应次数的重试
查看“日志”是否触发相应次数的重试事件“core.loadretry”、请求开始“core.loadstart”和请求完成“core.loadcomplete”' + desc += '
拉流最大探测时间: 对于存在音、视频标识,但是一直拉取不到音、视频数据的case, 拉流数据超过设置的时长后当做无音频或者无视频起播' + show(doLoadTimeout, true) + show(doRetryCount, true) + show(doRetryDelay, true) + show(doAnalyzeDuration, true) + highlight(dlEvent) + logFilter = function (name) { + return name === 'core.loadretry' || name === 'core.loadcomplete' || name === 'core.loadstart' + } + } + break; + case 2: { + desc = '设置高亮参数,并观察状态区域中的“当前时间之前缓存时长”是否与设置的值差不多(允许一定的误差)' + show(doBufferBehind, true) + highlight(dsBuffer) + } + break; + case 3: { + desc = '设置高亮两个参数,观察视频是否自动播放(自动播放失败,点击播放按钮可以正常播放),如果是静音自动播放则必须自动播放成功' + show(doAutoplay, true) + show(doAutoplayMuted, true) + } + break; + case 4: { + desc = '点击 API 方法中的 “切换url” 和 “设置url” 按钮,观察是否切换视频成功,并且可以播放,暂停' + dbSwitchUrl.style.background="rgb(245, 158, 11)" + dbSetUrl.style.background="rgb(245, 158, 11)" + } + break; + case 5: { + desc = '设置该参数,然后在直播中暂停,等待设置该参数差不多的时间,然后点播放按钮,观察直播是否拉流(出现黑屏)。等待小于该时间点播放可以连续播放不会重新拉流(不黑屏)' + + '
如果设置了无缝重拉流则不会黑屏' + show(doDisconnectTime, true) + show(doSeamlesslyReload, true) + } + break; + } + dTestPointDesc.innerHTML = desc + + inp(doUrl).value = opts.url + inp(doAutoplay).checked = opts.autoplay + inp(doAutoplayMuted).checked = opts.autoplayMuted + inp(doSeamlesslyReload).checked = opts.seamlesslyReload + inp(doLoadTimeout).value = opts.loadTimeout + inp(doRetryCount).value = opts.retryCount + inp(doRetryDelay).value = opts.retryDelay + inp(doAnalyzeDuration).value = opts.analyzeDuration + inp(doFetchOptions).value = opts.fetchOptions || '' + inp(doBufferBehind).value = opts.bufferBehind + inp(doMaxJumpDistance).value = opts.maxJumpDistance + inp(doTargetLatency).value = opts.targetLatency + inp(doMaxLatency).value = opts.maxLatency + inp(doDisconnectTime).value = opts.disconnectTime + inp(doMaxReaderInterval).value = opts.maxReaderInterval + + inp(doUrl).onchange = function () { updateOpts('url', this.value, 'string') } + inp(doAutoplay).onchange = function () { updateOpts('autoplay', this.checked) } + inp(doAutoplayMuted).onchange = function () { updateOpts('autoplayMuted', this.checked) } + inp(doSeamlesslyReload).onchange = function () { updateOpts('seamlesslyReload', this.checked) } + inp(doLoadTimeout).onchange = function () { updateOpts('loadTimeout', this.value, 'number') } + inp(doRetryCount).onchange = function () { updateOpts('retryCount', this.value, 'number') } + inp(doRetryDelay).onchange = function () { updateOpts('retryDelay', this.value, 'number') } + inp(doAnalyzeDuration).onchange = function () { updateOpts('analyzeDuration', this.value, 'number') } + inp(doFetchOptions).onchange = function () { updateOpts('fetchOptions', this.value, 'object') } + inp(doBufferBehind).onchange = function () { updateOpts('bufferBehind', this.value, 'number') } + inp(doMaxJumpDistance).onchange = function () { updateOpts('maxJumpDistance', this.value, 'number') } + inp(doTargetLatency).onchange = function () { updateOpts('targetLatency', this.value, 'number') } + inp(doMaxLatency).onchange = function () { updateOpts('maxLatency', this.value, 'number') } + inp(doDisconnectTime).onchange = function () { updateOpts('disconnectTime', this.value, 'number') } + inp(doMaxReaderInterval).onchange = function () { updateOpts('maxReaderInterval', this.value, 'number') } + + initPlayer() + + dbResetOpt.onclick = resetOpts + dbApplyOpt.onclick = initPlayer + dbPlay.onclick = function () { player.play() } + dbPause.onclick = function () { player.pause() } + dbReplay.onclick = function () { player.replay() } + dbDestroy.onclick = function () { player.destroy() } + dbSwitchUrl.onclick = function () { + var url = window.prompt('设置的 url 地址') + if (!url) return + var seamless = window.prompt('是否无缝切换 “true” 或 “false”', false) + player.switchURL(url, seamless.trim() === 'true') + } + dbSetUrl.onclick = function () { + var url = window.prompt('设置的 url 地址') + if (url) player.src = url + } + dbSeek.onclick = function () { + var time = window.prompt('设置的跳转到的事件点') + if (time == null) return + time = Number(time) + if (isNaN(time)) return + player.seek(time) + } + + dTestPoint.onchange = function () { + localStorage.setItem('xg:test:flv:point', this.value) + resetOpts() + } + + setTimeout(function () { + var lastPlayback = null + var fps = 0 + var prevTime = 0 + setInterval(function () { + if (player && player.plugins.flv) { + var t = player.currentTime + prevTime = t + var flv = player.plugins.flv.core + var buf = flv.bufferInfo() + var pq = flv.playbackQuality() + var sp = flv.speedInfo() + if (lastPlayback) { + fps = Math.round((pq.totalVideoFrames - lastPlayback.totalVideoFrames) / (pq.creationTime - lastPlayback.creationTime) * 1000) + } + lastPlayback = pq + + dsBuffer.innerHTML = + '

当前时间:' + t + 's

' + + '

剩余缓存时长:' + buf.remaining + 's

' + + '

当前时间之前缓存时长:' + buf.behind + 's

' + + '

总缓存时长:' + buf.length + 's

' + + '

buffers:' + JSON.stringify(buf.buffers) + '

' + + dsFrame.innerHTML = + '

总渲染帧数:' + pq.totalVideoFrames + '

' + + '

掉帧数量:' + pq.droppedVideoFrames + '

' + + '

fps:' + fps + '

' + + dsSpeed.innerHTML = + '

当前速度:' + Math.round(sp.speed / (8 * 1024)) + 'KB/s

' + + '

平均速度:' + Math.round(sp.avgSpeed / (8 * 1024)) + 'KB/s

' + + } + }, 1000) + }) +} diff --git a/fixtures/hls/index.js b/fixtures/hls/index.js new file mode 100644 index 00000000..70c52b8e --- /dev/null +++ b/fixtures/hls/index.js @@ -0,0 +1,398 @@ +import Player from '../../packages/xgplayer/src' +import HlsPlayer from '../../packages/xgplayer-hls/src' + +localStorage.setItem('xgd', 1) +function defaultOpt() { + return { + isLive: false, + autoplay: false, + autoplayMuted: false, + retryTimes: 3, + retryCount: 3, + retryDelay: 1000, + loadTimeout: 10000, + preloadTime: 10, + bufferBehind: 10, + maxJumpDistance: 3, + startTime: 0 + } +} +var cachedOpt = localStorage.getItem('xg:test:hls:opt') +try { cachedOpt = JSON.parse(cachedOpt) } catch (error) { cachedOpt = undefined } +var opts = Object.assign({ + url: 'https://test-streams.mux.dev/x36xhzz/url_0/193039199_mp4_h264_aac_hd_7.m3u8', +}, defaultOpt(), cachedOpt) +var testPoint = Number(localStorage.getItem('xg:test:hls:point')) +if (isNaN(testPoint)) testPoint = 0 + +window.onload = function () { + var dTestPoint = document.getElementById('test-point') + var dTestPointDesc = document.getElementById('test-point-desc') + + var doPresetUrl = document.getElementById('preset-url') + var doUrl = document.getElementById('url') + var doIsLive = document.getElementById('is-live') + var doAutoplay = document.getElementById('autoplay') + var doAutoplayMuted = document.getElementById('autoplay-muted') + var doLoadTimeout = document.getElementById('load-timeout') + var doRetryCount = document.getElementById('retry-count') + var doRetryDelay = document.getElementById('retry-delay') + var doFetchOptions = document.getElementById('fetch-options') + var doPreloadTime = document.getElementById('preload-time') + var doBufferBehind = document.getElementById('buffer-behind') + var doMaxJumpDistance = document.getElementById('max-jump-distance') + var doStartTime = document.getElementById('start-time') + var doTargetLatency = document.getElementById('target-latency') + var doMaxLatency = document.getElementById('max-latency') + var doDisconnectTime = document.getElementById('disconnect-time') + + var dbResetOpt = document.getElementById('reset-opt') + var dbApplyOpt = document.getElementById('apply-opt') + var dbPlay = document.getElementById('play') + var dbPause = document.getElementById('pause') + var dbReplay = document.getElementById('replay') + var dbDestroy = document.getElementById('destroy') + var dbSwitchUrl = document.getElementById('switch-url') + var dbSetUrl = document.getElementById('set-url') + var dbSeek = document.getElementById('seek') + + var dStreamsContainer = document.getElementById('streams') + var dStreamForce = document.getElementById('stream-force') + var dVideoStreams = document.getElementById('video-streams') + var dAudioStreams = document.getElementById('audio-streams') + + var dlEvent = document.getElementById('event') + var dlError = document.getElementById('error') + + var dsSpeed = document.getElementById('speed') + var dsFrame = document.getElementById('frame') + var dsBuffer = document.getElementById('buffer') + var dsOption = document.getElementById('option') + + dTestPoint.selectedIndex = testPoint + dsOption.innerHTML = '
' + JSON.stringify(opts, null, 2) + '
' + + function inp(d) { return d.getElementsByTagName('input')[0] } + + var player + + function updateOpts(key, value, type) { + if (type === 'object') { + try { + value = (new Function('x', 'return ' + value))() + } catch (error) { } + } + opts[key] = value + localStorage.setItem('xg:test:hls:opt', JSON.stringify(opts)) + dsOption.innerHTML = '
' + JSON.stringify(opts, null, 2) + '
' + } + function resetOpts() { + opts = Object.assign({ url: opts.url }, defaultOpt()) + localStorage.setItem('xg:test:hls:opt', JSON.stringify(opts)) + window.location.reload() + } + function initPlayer() { + if (player) { + player.destroy() + setTimeout(init, 100) + } else { + init() + } + function init() { + window.player = player = new Player({ + // mediaType: 'live-video', + el: document.getElementById('player'), + plugins: [HlsPlayer], + url: opts.url, + isLive: opts.isLive, + startTime: opts.startTime, + autoplay: opts.autoplay, + autoplayMuted: opts.autoplayMuted, + hls: Object.assign({}, opts, { + fetchOptions: Object.assign({ + referrer: 'no-referrer', + referrerPolicy: 'no-referrer' + }, opts.fetchOptions) + }) + }); + dlEvent.innerHTML = '' + dlError.innerHTML = '' + + function pushEvent(name, value, container) { + container = container || dlEvent + console.debug('[test]', name, value) + if (container === dlEvent && logFilter && !logFilter(name, value)) { + return + } + + if (name === 'core.metadataparsed') { + if (value.track?.samples?.length > 100) { + return { '_': '数据过大,请查看devtools' } + } + } + try { + value = JSON.stringify(value) + } catch (error) { + } + var record = document.createElement('div') + record.innerHTML = '
' + name + ' / ' + player.video.currentTime + '' + value + '
' + container.prepend(record) + } + + player.on('loadstart', function (event) { pushEvent('loadstart', event) }) + player.on('loadeddata', function (event) { pushEvent('loadeddata', event) }) + player.on('play', function (event) { pushEvent('play', event) }) + player.on('pause', function (event) { pushEvent('pause', event) }) + player.on('ended', function (event) { pushEvent('ended', event) }) + player.on('autoplay_was_prevented', function (event) { pushEvent('autoplay_was_prevented', event) }) + player.on('playing', function (event) { pushEvent('playing', event) }) + player.on('seeking', function (event) { pushEvent('seeking', event) }) + player.on('seeked', function (event) { pushEvent('seeked', event) }) + player.on('waiting', function (event) { pushEvent('waiting', event) }) + player.on('canplay', function (event) { pushEvent('canplay', event) }) + player.on('durationchange', function (event) { pushEvent('durationchange', event) }) + player.on('ready', function (event) { pushEvent('ready', event) }) + player.on('complete', function (event) { pushEvent('complete', event) }) + player.on('urlchange', function (event) { pushEvent('urlchange', event) }) + player.on('destroy', function (event) { pushEvent('destroy', event) }) + player.on('replay', function (event) { pushEvent('replay', event) }) + player.on('retry', function (event) { pushEvent('retry', event) }) + player.on('core_event', function (event) { pushEvent(event.eventName, event) }) + player.on('error', function (event) { pushEvent(event.errorType, event, dlError) }) + + dStreamForce.checked = true + function refreshStreams() { + var hls = player.plugins.hls.core + var streams = hls.streams + var currentStream = hls.currentStream + var streams = hls.streams + dStreamsContainer.style.display = 'none' + dVideoStreams.innerHTML = '' + dAudioStreams.innerHTML = '' + if (streams.length > 2) { + dStreamsContainer.style.display = 'block' + streams.forEach(s => { + var texts = [] + if (s.width && s.height) texts.push(s.width + 'x' + s.height) + if (s.videoCodec) texts.push(s.videoCodec.split('.')[0]) + if (s.bitrate) texts.push(Math.round(s.bitrate / 8192) + 'KBs') + var button = document.createElement('button') + button.setAttribute('class', 'py-1 px-2 mt-1 text-white shadow-md mr-2 ' + (currentStream.id === s.id ? 'bg-red-500' : 'bg-green-500')) + button.textContent = texts.join('/') + button.onclick = function () { + hls.switchStream(s.id, dStreamForce.checked).then(function (s) { + if (s && s.segments.length && !hls.isLive) { + refreshStreams() + } + }) + } + dVideoStreams.appendChild(button) + }) + } + if (currentStream.audioStreams.length > 2) { + dStreamsContainer.style.display = 'block' + var currentAudio = currentStream.currentAudioStream + currentStream.audioStreams.forEach(s => { + var texts = [] + if (s.name) texts.push(s.name) + if (s.lang) texts.push(s.lang) + if (s.channels) texts.push(s.channels) + var button = document.createElement('button') + button.setAttribute('class', 'py-1 px-2 mt-1 text-white shadow-md mr-2 ' + (currentAudio.id === s.id ? 'bg-red-500' : 'bg-green-500')) + button.textContent = texts.join('/') + button.onclick = function () { + hls.switchAudioStream(s.id, dStreamForce.checked).then(function (s) { + if (s && s.segments.length && !hls.isLive) { + refreshStreams() + } + }) + } + dAudioStreams.appendChild(button) + }) + } + } + player.on('core_event', function (event) { + if (event.eventName === HlsPlayer.EVENT.STREAM_PARSED) { + refreshStreams() + } + }) + } + } + + function highlight(d) { d.classList.add('bg-yellow-300') } + function show(d, h) { d.style.display = 'block'; if (h) highlight(d) } + + var logFilter + var desc = '' + switch (testPoint) { + case 0: { + show(doAutoplay) + show(doAutoplayMuted) + show(doLoadTimeout) + show(doRetryCount) + show(doRetryDelay) + show(doFetchOptions) + show(doPreloadTime) + show(doBufferBehind) + show(doMaxJumpDistance) + show(doStartTime) + show(doTargetLatency) + show(doMaxLatency) + show(doDisconnectTime) + } + break; + case 1: { + desc = '设置高亮三个参数。
查看"开发者工具"的Network面板是否进行相应次数的重试
查看“日志”是否触发相应次数的重试事件“core.loadretry”、请求开始“core.loadstart”和请求完成“core.loadcomplete”' + show(doLoadTimeout, true) + show(doRetryCount, true) + show(doRetryDelay, true) + highlight(dlEvent) + logFilter = function (name) { + return name === 'core.loadretry' || name === 'core.loadcomplete' || name === 'core.loadstart' + } + } + break; + case 2: { + desc = '设置高亮两个参数,并观察状态区域中的“剩余缓存时长”和“当前时间之前缓存时长”是否与设置的值差不多(允许一定的误差)' + show(doPreloadTime, true) + show(doBufferBehind, true) + highlight(dsBuffer) + } + break; + case 3: { + desc = '设置高亮两个参数,观察视频是否自动播放(自动播放失败,点击播放按钮可以正常播放),如果是静音自动播放则必须自动播放成功' + show(doAutoplay, true) + show(doAutoplayMuted, true) + } + break; + case 4: { + desc = '点击 API 方法中的 “切换url” 和 “设置url” 按钮,观察是否切换视频成功,并且可以播放,暂停' + dbSwitchUrl.style.background = "rgb(245, 158, 11)" + dbSetUrl.style.background = "rgb(245, 158, 11)" + } + break; + case 5: { + desc = '设置点播的开始时间参数,查看视频是否在该时间点播放。点击 “seek” 按钮或拖动进度条,进行跳转播放时间,观察是否跳转成功。视频播放结束是否出现重播按钮,点击可以重播,点击重播按钮也可以重播。' + show(doStartTime, true) + dbReplay.style.background = "rgb(245, 158, 11)" + dbSeek.style.background = "rgb(245, 158, 11)" + } + break; + case 6: { + desc = '设置该参数,然后在直播中暂停,等待设置该参数差不多的时间,然后点播放按钮,观察直播是否拉流(出现黑屏)。等待小于该时间点播放可以连续播放不会重新拉流(不黑屏)' + show(doDisconnectTime, true) + } + break; + } + dTestPointDesc.innerHTML = desc + + inp(doUrl).value = opts.url + inp(doIsLive).checked = opts.isLive + inp(doAutoplay).checked = opts.autoplay + inp(doAutoplayMuted).checked = opts.autoplayMuted + inp(doLoadTimeout).value = opts.loadTimeout + inp(doRetryCount).value = opts.retryCount + inp(doRetryDelay).value = opts.retryDelay + inp(doFetchOptions).value = opts.fetchOptions || '' + inp(doPreloadTime).value = opts.preloadTime + inp(doBufferBehind).value = opts.bufferBehind + inp(doMaxJumpDistance).value = opts.maxJumpDistance + inp(doStartTime).value = opts.startTime + inp(doTargetLatency).value = opts.targetLatency + inp(doMaxLatency).value = opts.maxLatency + inp(doDisconnectTime).value = opts.disconnectTime + + inp(doUrl).onchange = function () { updateOpts('url', this.value) } + inp(doIsLive).onchange = function () { updateOpts('isLive', this.checked) } + inp(doAutoplay).onchange = function () { updateOpts('autoplay', this.checked) } + inp(doAutoplayMuted).onchange = function () { updateOpts('autoplayMuted', this.checked) } + inp(doLoadTimeout).onchange = function () { updateOpts('loadTimeout', this.value) } + inp(doRetryCount).onchange = function () { updateOpts('retryCount', this.value) } + inp(doRetryDelay).onchange = function () { updateOpts('retryDelay', this.value) } + inp(doFetchOptions).onchange = function () { updateOpts('fetchOptions', this.value, 'object') } + inp(doPreloadTime).onchange = function () { updateOpts('preloadTime', this.value) } + inp(doBufferBehind).onchange = function () { updateOpts('bufferBehind', this.value) } + inp(doMaxJumpDistance).onchange = function () { updateOpts('maxJumpDistance', this.value) } + inp(doStartTime).onchange = function () { updateOpts('startTime', this.value) } + inp(doTargetLatency).onchange = function () { updateOpts('targetLatency', this.value) } + inp(doMaxLatency).onchange = function () { updateOpts('maxLatency', this.value) } + inp(doDisconnectTime).onchange = function () { updateOpts('disconnectTime', this.value) } + + doPresetUrl.getElementsByTagName('select')[0].onchange = function () { + if (this.value) { + updateOpts('url', this.value) + inp(doUrl).value = this.value + initPlayer() + } + } + + initPlayer() + + dbResetOpt.onclick = resetOpts + dbApplyOpt.onclick = initPlayer + dbPlay.onclick = function () { player.play() } + dbPause.onclick = function () { player.pause() } + dbReplay.onclick = function () { player.replay() } + dbDestroy.onclick = function () { player.destroy() } + dbSwitchUrl.onclick = function () { + var url = window.prompt('设置的 url 地址') + if (!url) return + var startTime = window.prompt('[点播]开始播放时间点', 0) + startTime = Number(startTime) + if (isNaN(startTime)) startTime = 0 + player.switchURL(url, startTime) + } + dbSetUrl.onclick = function () { + var url = window.prompt('设置的 url 地址') + if (url) player.src = url + } + dbSeek.onclick = function () { + var time = window.prompt('设置的跳转到的事件点') + if (time == null) return + time = Number(time) + if (isNaN(time)) return + player.seek(time) + } + + dTestPoint.onchange = function () { + localStorage.setItem('xg:test:hls:point', this.value) + resetOpts() + } + + setTimeout(function () { + var lastPlayback = null + var fps = 0 + var prevTime = 0 + setInterval(function () { + if (player && player.plugins.hls) { + var t = player.currentTime + prevTime = t + var hls = player.plugins.hls.core + var buf = hls.bufferInfo() + var pq = hls.playbackQuality() + var sp = hls.speedInfo() + if (lastPlayback) { + fps = Math.round((pq.totalVideoFrames - lastPlayback.totalVideoFrames) / (pq.creationTime - lastPlayback.creationTime) * 1000) + } + lastPlayback = pq + + dsBuffer.innerHTML = + '

当前时间:' + t + 's

' + + '

剩余缓存时长:' + buf.remaining + 's

' + + '

当前时间之前缓存时长:' + buf.behind + 's

' + + '

总缓存时长:' + buf.length + 's

' + + '

buffers:' + JSON.stringify(buf.buffers) + '

' + + dsFrame.innerHTML = + '

总渲染帧数:' + pq.totalVideoFrames + '

' + + '

掉帧数量:' + pq.droppedVideoFrames + '

' + + '

fps:' + fps + '

' + + dsSpeed.innerHTML = + '

当前速度:' + Math.round(sp.speed / (8 * 1024)) + 'KB/s

' + + '

平均速度:' + Math.round(sp.avgSpeed / (8 * 1024)) + 'KB/s

' + + } + }, 1000) + }) +} diff --git a/fixtures/mp4/index.js b/fixtures/mp4/index.js new file mode 100644 index 00000000..09c679a1 --- /dev/null +++ b/fixtures/mp4/index.js @@ -0,0 +1,25 @@ +// import '../../packages/xgplayer-livevideo/src' +// import MP4Player from '../../packages/xgplayer-mp4/src' +import Player from '../../packages/xgplayer/src'; +import Mp4Player from '../../packages/xgplayer-mp4/src'; + +// console.log(Player, Mp4Player) +let player=new Player({ + id: 'vs', + autoplay: true, + volume: 0.3, + url:'./err.mp4', + poster: "//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/poster.jpg", + playsinline: true, + thumbnail: { + pic_num: 44, + width: 160, + height: 90, + col: 10, + row: 10, + // urls: ['//lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo-thumbnail.jpg'], + }, + height: window.innerHeight, + width: window.innerWidth, + plugins: [Mp4Player] +}); diff --git a/fixtures/music/index.js b/fixtures/music/index.js new file mode 100644 index 00000000..71915b76 --- /dev/null +++ b/fixtures/music/index.js @@ -0,0 +1,84 @@ +// import Player from 'xgplayer' +// // import '../../packages/xgplayer/dist/index.min.css' +// import MusicPreset, { Music, Analyze, Lyric } from '../../packages/xgplayer-music/src' +// import '../../packages/xgplayer-music/dist/index.min.css' +// window.player = new Player({ +// id: 'vs', +// url: '', +// loop: false, +// autoplay: false, +// preloadTime:20, +// width: "100%", +// height: 70, +// controls: { +// mode: 'flex', +// initShow: true +// }, +// music: { +// offline: true, +// list: [{ +// vid: '1', +// src: './audio/1.mp3', +// title: '11111.mp3', +// poster: "https://p9-pc-sign.douyinpic.com/tos-cn-p-0015/ocAY2PfD40Q8Z5SHAdMg7IC8ebbRe7BEBn0CAa~tplv-dy-resize-origshort-autoq-75:330.jpeg?x-expires=1988096400&x-signature=jWrbLPoE8ow7fdCYyyeUWw2Oaf0%3D&from=3213915784&s=PackSourceEnum_FEED&se=false&sc=cover&biz_tag=pcweb_cover&l=20230103173048B1113E7290F15B007F0B" +// }, { +// vid: '2', +// src: './audio/2.mp3', +// title: '22222.mp3', +// poster: "https://p9-pc-sign.douyinpic.com/image-cut-tos-priv/12f35be1484d36bf1430bb46e4874589~tplv-dy-resize-origshort-autoq-75:330.jpeg?x-expires=1988096400&x-signature=wl1p8fpK09dXkS7uWO1qG1GRSPU%3D&from=3213915784&s=PackSourceEnum_FEED&se=false&sc=cover&biz_tag=pcweb_cover&l=20230103173048B1113E7290F15B007F0B" +// }, { +// vid: '3', +// src: './audio/3.mp3', +// title: '33333.mp3', +// poster: "https://p3-pc-sign.douyinpic.com/image-cut-tos-priv/9cd43ffa45bfe6b52e2847c8071f9b6f~tplv-dy-resize-origshort-autoq-75:330.jpeg?x-expires=1988096400&x-signature=I7QrHLEnZfMDjJtxjXPf0M7L00A%3D&from=3213915784&s=PackSourceEnum_FEED&se=false&sc=cover&biz_tag=pcweb_cover&l=20230103173048B1113E7290F15B007F0B" +// }, { +// vid: '4', +// src: './audio/4.mp3', +// title: '444444.mp3', +// poster: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/music/poster-small.jpeg' +// }] +// }, +// marginControls: true, +// // presets: ['default', MusicPreset], +// plugins: [Music], +// ignores: ['start', 'fullscreen', 'cssfullscreen', 'playbackrate'], +// mediaType: 'audio', +// videoConfig: { +// crossOrigin: 'anonymous' +// }, +// mediaType: 'audio' +// }); +// window.player.on('loadstart', (data) => { +// console.log('loadstart') +// }) + +// window.player.on('loadeddata', (data) => { +// console.log('loadeddata') +// }) + +// window.player.on('xglog', (data) => { +// console.log('[xglog]', data) +// }) + +// window.player.on('user_action', (data) => { +// console.log('[user_action]', data) +// }) + + +// function init() { +// window.analyze = new Analyze(player, document.querySelector('canvas'), { +// bgColor: 'rgba(0,0,0,0.65)', +// stroke: 1, + +// }) +// console.log('Analyze.MODES', Analyze.MODES) +// // analyze.mode = Analyze.MODES.DOU +// // player.on('playing', function(){ +// // player.lyric (lyric, document.querySelector('#gc')); +// // player.__lyric__.show(); +// // player.mode = 2; +// // }); +// // document.getElementById("canvas").width = window.innerWidth; +// // document.getElementById("canvas").height = window.innerHeight * 0.36; +// } +// init() \ No newline at end of file diff --git a/fixtures/xgplayer/index.html b/fixtures/xgplayer/index.html new file mode 100644 index 00000000..ec97a9c3 --- /dev/null +++ b/fixtures/xgplayer/index.html @@ -0,0 +1,94 @@ + + + + + + + + Document + + + + + +
+
+
+ + + + + +
+
+
+

current lang:

+
+ +
+

log info:

+
+
+
+ + + + diff --git a/fixtures/xgplayer/index.js b/fixtures/xgplayer/index.js new file mode 100644 index 00000000..fa61f4db --- /dev/null +++ b/fixtures/xgplayer/index.js @@ -0,0 +1,283 @@ +import Player from '../../packages/xgplayer/src/index' +import { TextTrack } from '../../packages/xgplayer/src/index' +import { I18N } from '../../packages/xgplayer/src' + +// 全局配置语言 +I18N.extend([ + { + LANG: 'zh', + TEXT: { + PAUSE_TIPS: '暂停0', + PLAY_TIPS: '起播0' + } + }, + { + LANG: 'en', + TEXT: { + PAUSE_TIPS: 'Pause0', + PLAY_TIPS: 'Play0' + } + } +]) + +window.player = null +window.player1 = null +function init(index = 0, config = {}) { + const p = `player${index}` + if (window[p]) { + window[p].destroy() + window[p] = null + } + window[p] = new Player({ + id: 'video' + index, + url: + [{src:'https://lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo-720p.mp4'}, + {src:'https://lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo-720p.mp4'}, + {src:'https://lf3-static.bytednsdoc.com/obj/eden-cn/nupenuvpxnuvo/xgplayer_doc/xgplayer-demo-720p.mp4'}], + DynamicBg: { + disable: false + }, + marginControls: false, + loop: false, + autoplay: false, + autoplayMuted: true, + videoInit: true, + preloadTime: 20, + width: '100%', + ignores:['playbackrate'], + plugins: [TextTrack], + // controls: { + // // mode: 'normal', + // // initShow: true + // }, + progress: { + // root: document.getElementById('controls0') + }, + volume: { + position: 'rootTop' + }, + mobile: { + // gestureX: false + }, + texttrack: { + debugger: false, + list: [{ + label: '双语', + language: 'double', + id: '0', + isDefault: true, + url: '../subtitle/vtt/double.vtt', // [{url: './vtt/cn1.vtt'}, {url: './vtts/1-3.vtt'}, {url: './vtts/1-2.vtt'},{url: './vtts/1.vtt'}, {url: './vtts/2.vtt'}, {url: './vtts/3.vtt'},{url: './vtts/4.vtt'}, {url: './vtts/5.vtt'}, {url: './vtts/6.vtt'},{url: './vtts/7.vtt'},{url: './vtts/8.vtt'}] + // url: 'https://sf3-xgcdn-tos.pstatp.com/obj/tos-cn-o-0004/bc9bdde6738d4865a69da7f5afaafc87' + // url: './textTrack-1.vtt?de3fd3', + //url: 'http://lf1-xgcdn-tos.pstatp.com/obj/tos-cn-o-0004/52ce3882d70941d5b660913cbd83d969' + }, { + label: '中文', + language: 'cn', + id: '1', + isDefault: undefined, + //url: 'http://tosv.byted.org/obj/tos-cn-o-0004/0edaf77715f44faebe2e348f5157280a' + url: '../subtitle/vtt/cn.vtt' + //url: 'http://lf1-xgcdn-tos.pstatp.com/obj/tos-cn-o-0004/52ce3882d70941d5b660913cbd83d969' + //url: './ass/cn.ass' + }, { + label: '英文', + url: '../subtitle/vtt/en.vtt', + //url: 'http://lf6-xgcdn-tos.pstatp.com/obj/tos-cn-o-0004/d04fa4122dac42d69e8233a4dfda82fe', + // url: './ass/double.ass', + id: '2', + isDefault: false, + language: 'en' + }], + updateMode: 'vod', + isDefaultOpen: true, + mode: 'external', + }, + definition: { + position: 'controlsLeft', + list: [ + { + url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4', + definition: '360p', + text: '标清 360P', + iconText: '标清' + }, + { + url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-480p.mp4', + definition: '480p', + text: { + zh: '高清 480P', + en: '480P' + }, + iconText: '高清' + }, + { + url: '//sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4', + definition: '720p', + text: { + zh: '超清 720P', + en: '720P' + }, + iconText: '超清' + } + ], + defaultDefinition: '360p', + isItemClickHide: false + }, + // 'definition': { + // 'text': '清晰度切换插件', + // 'type': 'array', + // 'checked': true, + // 'list': [ + // { name: '超清', url: './media/msdv3.mp4' }] + // }, + // width: 300, + height: 500, + ...config + }) + + // window[p].usePluginHooks('progresspreview', 'transformTime', (plugin, time) => { + // plugin.setTimeContent(`~~${(time)}~~`) + + // // 阻止插件内部默认钩子逻辑 + // return false + // }) + + window[p].on('download_speed_change', data => { + console.log('[download_speed_change]', data) + // addLog(index, `[download_speed_change] speed:${data.speed}, realTimeSpeed:${data.realTimeSpeed || 0}`) + }) + + window[p].on('xglog', data => { + console.log('[xglog]', data) + if (data.eventType === 'firstFrame' || data.eventType === 'waitingEnd') { + addLog( + index, + `[xglog] eventType:${data.eventType} ${data.costTime || 0} ${data.endType || ''}` + ) + } + }) + + window.currentTime = 0 + window[p].on('timeupdate', () => { + if (window[p].currentTime > currentTime) { + currentTime = window[p].currentTime + } + }) + + // window[p].usePluginHooks('progress', 'dragstart', (plugin, event, data) =>{ + // console.log('progress', data) + // // TODO + // if (data.currentTime > currentTime) { + // return false + // } + // }) + + // window[p].usePluginHooks('progress', 'drag', (plugin, event, data) =>{ + // // TODO + // if (data.currentTime > currentTime) { + // return false + // } + // }) + + window[p].on('user_action', data => { + console.log('[user_action]', data) + }) +} + +init() + +// init(1, { +// i18n: [ +// { +// LANG: 'zh', +// TEXT: { +// PLAY_TIPS: '播放1', +// PAUSE_TIPS: '暂停1', +// PAUSE_TIPS1: '测试1', +// CSSFULLSCREEN_TIPS: '网页全屏1' +// } +// }, +// { +// LANG: 'en', +// TEXT: { +// PLAY_TIPS: 'Play1', +// PAUSE_TIPS: 'Pause1', +// PAUSE_TIPS1: 'test1', +// CSSFULLSCREEN_TIPS: 'pagefullscreen1' +// } +// } +// ], +// enableContextmenu: true +// }) + +function addLog(index, mse) { + const logDom = document.getElementById(`js-show-log${index}`) + const p = document.createElement('p') + p.innerHTML = mse + logDom.appendChild(p) +} + +function clearLog(index) { + const logDom = document.getElementById(`js-show-log${index}`) + const ps = logDom.getElementsByTagName('p') + let _len = ps.length + while (ps.length > 0) { + logDom.removeChild(ps[0]) + _len-- + } +} + +function changeLang(index) { + const logDom = document.getElementById(`js-show-lang${index}`) + const p = logDom.getElementsByTagName('p') + const player = window[`player${index}`] + if (player.lang === 'zh') { + player.lang = 'en' + } else if (player.lang === 'en') { + player.lang = 'zh' + } + if (p.length === 0) { + const p = document.createElement('p') + p.innerHTML = player.lang + logDom.appendChild(p) + } else { + p[0].innerHTML = player.lang + } + console.log('p', p) +} + +function playNext(index) { + console.log('playNext', index) + const p = `player${index}` + const config = {} +} + +function destroy(index) { + console.log('destroy', index) + const p = `player${index}` + window[p] && window[p].destroy() +} + +window.changeLang = changeLang +window.clearLog = clearLog +window.addLog = addLog +window.playNext = playNext +window.destroy = destroy +window.init = init +window.createDot = (index) => { + const player = window[`player${index}`] + const time = parseInt(Math.random(1) * player.duration, 10) + const duration = parseInt(Math.random(1) * 30 + 10, 10) + const ISPOT = { + time: time, // 进度条在此时间戳打点 单位为s + text: '', // 打点处的自定义文案 + id: time, // 标记唯一标识,用于删除的时候索引 + duration: duration, // 进度条标识点的时长 默认1s【可选】单位为s + color: '#fff', // 进度条标识点的显示颜色【可选】 + style: {}, // 指定样式 + width: 6, + height: 6 + } + console.log(ISPOT) + player.plugins.progresspreview.createDot(ISPOT) +}