refactor: 💡 add fixtures

This commit is contained in:
gemstone 2023-05-15 15:19:14 +08:00
parent 26931f06ec
commit 8909a5e81e
7 changed files with 1207 additions and 0 deletions

1
.gitignore vendored
View File

@ -85,6 +85,7 @@ packages/*/types/
.DS_Store
fixtures/*/*
!fixtures/*/index.html
!fixtures/*/index.js
#http-server cert
*.pem

322
fixtures/flv/index.js Normal file
View File

@ -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 = '<pre>' + JSON.stringify(opts, null, 2) + '</pre>'
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 = '<pre>' + JSON.stringify(opts, null, 2) + '</pre>'
}
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 = '<div class="mb-2"><span class="text-base pr-2 bg-green-500 text-white">' + name + ' / ' + player.video.currentTime + '</span>' + value + '</div>'
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 = '设置高亮四个参数。<br>查看"开发者工具"的Network面板是否进行相应次数的重试<br>查看“日志”是否触发相应次数的重试事件“core.loadretry”、请求开始“core.loadstart”和请求完成“core.loadcomplete”'
desc += '<br>拉流最大探测时间: 对于存在音、视频标识但是一直拉取不到音、视频数据的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 = '设置该参数,然后在直播中暂停,等待设置该参数差不多的时间,然后点播放按钮,观察直播是否拉流(出现黑屏)。等待小于该时间点播放可以连续播放不会重新拉流(不黑屏)' +
'<br> 如果设置了无缝重拉流则不会黑屏'
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 =
'<p>当前时间:' + t + 's</p>' +
'<p>剩余缓存时长:' + buf.remaining + 's</p>' +
'<p>当前时间之前缓存时长:' + buf.behind + 's</p>' +
'<p>总缓存时长:' + buf.length + 's</p>' +
'<p>buffers' + JSON.stringify(buf.buffers) + '</p>'
dsFrame.innerHTML =
'<p>总渲染帧数:' + pq.totalVideoFrames + '</p>' +
'<p>掉帧数量:' + pq.droppedVideoFrames + '</p>' +
'<p>fps:' + fps + '</p>'
dsSpeed.innerHTML =
'<p>当前速度:' + Math.round(sp.speed / (8 * 1024)) + 'KB/s</p>' +
'<p>平均速度:' + Math.round(sp.avgSpeed / (8 * 1024)) + 'KB/s</p>'
}
}, 1000)
})
}

398
fixtures/hls/index.js Normal file
View File

@ -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 = '<pre>' + JSON.stringify(opts, null, 2) + '</pre>'
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 = '<pre>' + JSON.stringify(opts, null, 2) + '</pre>'
}
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 = '<div class="mb-2"><span class="text-base pr-2 bg-green-500 text-white">' + name + ' / ' + player.video.currentTime + '</span>' + value + '</div>'
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 = '设置高亮三个参数。<br>查看"开发者工具"的Network面板是否进行相应次数的重试<br>查看“日志”是否触发相应次数的重试事件“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 =
'<p>当前时间:' + t + 's</p>' +
'<p>剩余缓存时长:' + buf.remaining + 's</p>' +
'<p>当前时间之前缓存时长:' + buf.behind + 's</p>' +
'<p>总缓存时长:' + buf.length + 's</p>' +
'<p>buffers' + JSON.stringify(buf.buffers) + '</p>'
dsFrame.innerHTML =
'<p>总渲染帧数:' + pq.totalVideoFrames + '</p>' +
'<p>掉帧数量:' + pq.droppedVideoFrames + '</p>' +
'<p>fps:' + fps + '</p>'
dsSpeed.innerHTML =
'<p>当前速度:' + Math.round(sp.speed / (8 * 1024)) + 'KB/s</p>' +
'<p>平均速度:' + Math.round(sp.avgSpeed / (8 * 1024)) + 'KB/s</p>'
}
}, 1000)
})
}

25
fixtures/mp4/index.js Normal file
View File

@ -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]
});

84
fixtures/music/index.js Normal file
View File

@ -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()

View File

@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="referrer" content="no-referrer" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!-- <script src="https://unpkg.com/vconsole/dist/vconsole.min.js"></script>
<script>
var ua = navigator.userAgent.toLowerCase()
console.log('ua', ua, (ua.indexOf('mobile') > -1 || ua.indexOf('ipad') > -1) && location.href.indexOf('vconsole=1') > -1)
if ((ua.indexOf('mobile') > -1 || ua.indexOf('ipad') > -1) && location.href.indexOf('vconsole=1') > -1) {
var vConsole = new window.VConsole();
}
</script> -->
<style>
.pannel {
margin: 20px 0;
padding: 20px;
background: #72a0c8;
}
.message-pannel {
margin-top: 20px;
color: #333a3c;
}
.message-info {
margin-top: 20px;
border: 1px solid #728bb8;
padding: 10px 10px;
}
h4 {
margin-block-start: 0.2em;
margin-block-end: 0.2em;
}
p {
line-height: 18px;
display: block;
line-height: 16px;
/* margin: 10px; */
margin-block-start: 0.4em;
margin-block-end: 0.4em;
}
.ext-controls0 {
height: 80px !important;
background-color: #000;
}
</style>
</head>
<body>
<div id="video0"></div>
<div class="pannel">
<div class="tool">
<button type="submit" class="btn" id="js-destroy0" onclick="window.destroy(0)">
销毁
</button>
<button type="submit" class="btn" id="js-reinit0" onclick="window.init(0)">
重新初始化
</button>
<button type="submit" class="btn" id="js-playnext0" onclick="window.playNext(0)">
播放下一个
</button>
<button
type="submit"
class="btn"
id="js-changelang0"
onclick="window.changeLang(0)"
>
切换语言
</button>
<button
type="submit"
class="btn"
id="js-changelang0"
onclick="window.createDot(0)"
>
添加预览点
</button>
</div>
<div class="message-pannel">
<div class="message-info" id="js-show-lang0">
<h4>current lang:</h4>
</div>
<div class="message-info" id="js-show-log0">
<h4>log info:</h4>
</div>
</div>
</div>
<script></script>
<script type="module" defer src="./index.js"></script>
</body>
</html>

283
fixtures/xgplayer/index.js Normal file
View File

@ -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)
}