mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-14 08:26:16 -08:00
test(dash): [PKG-DASH-05] checkpoint real SDK media and seek diagnostics
This commit is contained in:
1 parent
6840544e0e
commit
c4b5f5f454
47 files changed
+2216
-11
No files matched your search
@@ -0,0 +1,307 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs'
|
||||
import process from 'node:process'
|
||||
import { ensureArchive, hash, readMember } from '../../refactor/scripts/releases.mjs'
|
||||
import { compilePackage } from '../helpers/load.js'
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
const sdks = new Map()
|
||||
const media = new Map()
|
||||
let candidate
|
||||
let published
|
||||
let mediaManifest
|
||||
let pluginRelease
|
||||
|
||||
test.beforeAll(async () => {
|
||||
const baseline = JSON.parse(fs.readFileSync(new URL('../../refactor/baselines/dash-sdk.json', import.meta.url)))
|
||||
for (const sdk of baseline.sdks) {
|
||||
const archive = await ensureArchive(sdk.release)
|
||||
const bytes = readMember(archive, sdk.codeMember)
|
||||
assert.equal(hash(bytes), sdk.release.files[sdk.codeMember])
|
||||
sdks.set(sdk.release.version, { ...sdk, code: bytes.toString() })
|
||||
}
|
||||
pluginRelease = JSON.parse(fs.readFileSync(new URL('../../refactor/baselines/dash-control-release.json', import.meta.url))).release
|
||||
const bytes = readMember(await ensureArchive(pluginRelease), 'package/dist/artplayer-plugin-dash-control.js')
|
||||
assert.equal(hash(bytes), pluginRelease.files['package/dist/artplayer-plugin-dash-control.js'])
|
||||
published = bytes.toString()
|
||||
candidate = process.env.ARTPLAYER_DASH_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_DASH_ARTIFACT, 'utf8') : await compilePackage('artplayer-plugin-dash-control', 'umd')
|
||||
mediaManifest = JSON.parse(fs.readFileSync(new URL('./media/dash/manifest.json', import.meta.url)))
|
||||
for (const [name, expected] of Object.entries(mediaManifest.files)) {
|
||||
assert(/^[\w.-]+$/.test(name))
|
||||
const bytes = fs.readFileSync(new URL(`./media/dash/${name}`, import.meta.url))
|
||||
assert.equal(hash(bytes), expected.sha256)
|
||||
assert.equal(bytes.length, expected.bytes)
|
||||
media.set(name, bytes)
|
||||
}
|
||||
})
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
const state = await page.evaluate(() => {
|
||||
const art = window.art
|
||||
const dash = art?.dash || window.nativeDash
|
||||
let tracks
|
||||
let levels
|
||||
try {
|
||||
tracks = dash?.getTracksFor('audio').map(track => ({ id: track.id, index: track.index, lang: track.lang }))
|
||||
levels = (dash?.getRepresentationsByType?.('video') || dash?.getBitrateInfoListFor?.('video'))?.map(level => ({ id: level.id, qualityIndex: level.qualityIndex, height: level.height }))
|
||||
}
|
||||
catch (error) {
|
||||
levels = { error: error.message }
|
||||
}
|
||||
const video = art?.video || window.nativeVideo
|
||||
const buffered = video ? Array.from({ length: video.buffered.length }, (_, index) => [video.buffered.start(index), video.buffered.end(index)]) : []
|
||||
return { errors: window.sdkErrors, events: window.sdkEvents, mediaEvents: window.mediaEvents, nativePlay: window.nativePlay, tracks, levels, buffered, paused: video?.paused, seeking: video?.seeking, time: video?.currentTime, ready: art?.isReady, width: video?.videoWidth, height: video?.videoHeight, error: video?.error?.code, readyState: video?.readyState, destroyed: window.sdkDestroyed }
|
||||
}).catch(error => ({ error: error.message }))
|
||||
await testInfo.attach('dash-sdk-state', { contentType: 'application/json', body: JSON.stringify(state) })
|
||||
if (!page.isClosed()) {
|
||||
await page.evaluate(() => {
|
||||
if (window.art && !window.art.isDestroy)
|
||||
window.art.destroy()
|
||||
window.nativeDash?.destroy()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
async function loadSDK(page, version, core, testInfo) {
|
||||
const sdk = sdks.get(version)
|
||||
await page.goto(`/test/player.html?core=${core}`)
|
||||
await page.addScriptTag({ content: sdk.code })
|
||||
const capability = await page.evaluate(() => ({ sdk: window.dashjs.supportsMediaSource(), mse: typeof window.MediaSource, managed: typeof window.ManagedMediaSource, avc: Boolean(window.MediaSource?.isTypeSupported('video/mp4; codecs="avc1.42c01e"')), aac: Boolean(window.MediaSource?.isTypeSupported('audio/mp4; codecs="mp4a.40.2"')) }))
|
||||
await testInfo.attach('dash-sdk-inputs', { contentType: 'application/json', body: JSON.stringify({ sdk: sdk.release, codeMember: sdk.codeMember, pluginRelease, candidateSHA256: hash(candidate), candidate: process.env.ARTPLAYER_DASH_ARTIFACT || 'workspace source build', media: mediaManifest, capability }) })
|
||||
return capability
|
||||
}
|
||||
|
||||
async function openDash(page, version, core, plugin, testInfo) {
|
||||
const capability = await loadSDK(page, version, core, testInfo)
|
||||
test.skip(!capability.sdk, 'Actual SDK reports no MediaSource support; this engine has no DASH playback acceptance')
|
||||
await routeMedia(page)
|
||||
await page.addScriptTag({ content: plugin === 'published' ? published : candidate })
|
||||
await page.evaluate(() => {
|
||||
window.sdkErrors = []
|
||||
window.sdkEvents = []
|
||||
window.mediaEvents = []
|
||||
window.sdkDestroyed = 0
|
||||
window.art = new window.Artplayer({
|
||||
container: '.player',
|
||||
url: '/dash-fixture/master.mpd',
|
||||
muted: true,
|
||||
setting: true,
|
||||
plugins: [window.artplayerPluginDashControl({ quality: { control: true, setting: true }, audio: { control: true, setting: true } })],
|
||||
customType: { mpd(video, url, art) {
|
||||
if (art.dash) {
|
||||
art.dash.destroy()
|
||||
window.sdkDestroyed++
|
||||
}
|
||||
const dash = window.dashjs.MediaPlayer().create()
|
||||
art.dash = dash
|
||||
dash.updateSettings({ debug: { logLevel: 0 }, streaming: { abr: { initialBitrate: { video: 150 } }, buffer: { fastSwitchEnabled: true, bufferTimeDefault: 2, bufferTimeAtTopQuality: 2 } } })
|
||||
const events = window.dashjs.MediaPlayer.events
|
||||
dash.on(events.ERROR, event => window.sdkErrors.push({ code: event.error?.code, message: event.error?.message, event: event.type }))
|
||||
for (const name of ['STREAM_INITIALIZED', 'QUALITY_CHANGE_REQUESTED', 'QUALITY_CHANGE_RENDERED', 'TRACK_CHANGE_RENDERED', 'STREAM_TEARDOWN_COMPLETE', 'BUFFER_LEVEL_STATE_CHANGED', 'FRAGMENT_LOADING_COMPLETED', 'PLAYBACK_SEEKING', 'PLAYBACK_SEEKED']) {
|
||||
if (events[name])
|
||||
dash.on(events[name], event => window.sdkEvents.push({ name, at: performance.now(), mediaType: event.mediaType, oldQuality: event.oldQuality, newQuality: event.newQuality, state: event.state, url: event.request?.url, time: video.currentTime }))
|
||||
}
|
||||
dash.initialize(video, url, false)
|
||||
} },
|
||||
})
|
||||
const art = window.art
|
||||
for (const name of ['play', 'playing', 'pause', 'seeking', 'seeked', 'waiting', 'emptied', 'loadedmetadata', 'loadeddata', 'canplay', 'error']) {
|
||||
art.video.addEventListener(name, () => window.mediaEvents.push({ name, at: performance.now(), time: art.video.currentTime, paused: art.video.paused, readyState: art.video.readyState, buffered: Array.from({ length: art.video.buffered.length }, (_, index) => [art.video.buffered.start(index), art.video.buffered.end(index)]) }))
|
||||
}
|
||||
art.on('destroy', () => {
|
||||
art.dash.destroy()
|
||||
window.sdkDestroyed++
|
||||
})
|
||||
document.querySelector('#play').onclick = () => art.play()
|
||||
document.querySelector('#pause').onclick = () => art.pause()
|
||||
})
|
||||
}
|
||||
|
||||
async function routeMedia(page) {
|
||||
await page.route('**/dash-fixture/**', async (route) => {
|
||||
const name = new URL(route.request().url()).pathname.split('/').at(-1)
|
||||
const bytes = media.get(name)
|
||||
await route.fulfill({ status: bytes ? 200 : 503, body: bytes || 'Intentional DASH fixture failure', contentType: name.endsWith('.mpd') ? 'application/dash+xml' : 'video/mp4' })
|
||||
})
|
||||
}
|
||||
|
||||
for (const version of ['4.5.2', '5.2.1']) {
|
||||
test(`dash.js ${version}: actual SDK reports MSE capabilities and version`, async ({ page }, testInfo) => {
|
||||
const capability = await loadSDK(page, version, 'candidate', testInfo)
|
||||
// This capability probe creates no media resources; destroy requires initialize.
|
||||
const actual = await page.evaluate(() => window.dashjs.MediaPlayer().create().getVersion())
|
||||
expect(actual).toBe(version)
|
||||
if (capability.mse === 'undefined' && capability.managed === 'undefined')
|
||||
expect(capability.sdk).toBe(false)
|
||||
else
|
||||
expect(capability.sdk).toBe(true)
|
||||
})
|
||||
}
|
||||
|
||||
for (const core of ['published', 'candidate']) {
|
||||
for (const [plugin, version] of [['published', '4.5.2'], ['candidate', '4.5.2'], ['candidate', '5.2.1']]) {
|
||||
test(`${core} core / ${plugin} DASH / ${version}: real MPD decoding and UI selection`, async ({ page }, testInfo) => {
|
||||
await openDash(page, version, core, plugin, testInfo)
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
expect(await page.evaluate(() => window.art.dash.getTracksFor('audio').map(track => track.lang))).toEqual(['en', 'fr'])
|
||||
expect(await page.evaluate(() => window.art.video.getVideoPlaybackQuality().totalVideoFrames)).toBeGreaterThan(0)
|
||||
const control = page.locator('.art-control-dash-quality')
|
||||
await control.hover()
|
||||
await control.locator('.art-selector-item').filter({ hasText: /^180p$/ }).click()
|
||||
await expect.poll(() => page.evaluate(() => ({ auto: window.art.dash.getSettings().streaming.abr.autoSwitchBitrate.video, height: window.art.video.videoHeight }))).toEqual({ auto: false, height: 180 })
|
||||
await control.hover()
|
||||
await control.locator('.art-selector-item').filter({ hasText: /^Auto$/ }).click()
|
||||
expect(await page.evaluate(() => window.art.dash.getSettings().streaming.abr.autoSwitchBitrate.video)).toBe(true)
|
||||
const audio = page.locator('.art-control-dash-audio')
|
||||
await audio.hover()
|
||||
await audio.locator('.art-selector-item').filter({ hasText: /^fr$/ }).click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.dash.getCurrentTrackFor('audio').lang)).toBe('fr')
|
||||
await expect(audio.locator('.art-selector-value')).toHaveText('fr')
|
||||
expect(await page.evaluate(() => window.art.setting.find('dash-audio').tooltip)).toBe('fr')
|
||||
await page.locator('#pause').click()
|
||||
expect(await page.evaluate(() => window.art.video.paused)).toBe(true)
|
||||
await page.evaluate(() => {
|
||||
window.art.seek = 6
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(6.2)
|
||||
expect(await page.evaluate(() => window.sdkErrors)).toEqual([])
|
||||
await testInfo.attach('dash-sdk-playing', { contentType: 'image/png', body: await page.screenshot() })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (const version of ['4.5.2', '5.2.1']) {
|
||||
test(`dash.js ${version}: native SDK track switch and paused seek without ArtPlayer`, async ({ page }, testInfo) => {
|
||||
const capability = await loadSDK(page, version, 'candidate', testInfo)
|
||||
test.skip(!capability.sdk, 'Actual SDK reports no MediaSource support; no native DASH playback acceptance')
|
||||
await routeMedia(page)
|
||||
await page.evaluate(() => {
|
||||
const video = document.createElement('video')
|
||||
window.nativeVideo = video
|
||||
video.muted = true
|
||||
document.querySelector('.player').append(video)
|
||||
const dash = window.dashjs.MediaPlayer().create()
|
||||
window.nativeDash = dash
|
||||
window.sdkEvents = []
|
||||
window.sdkErrors = []
|
||||
window.mediaEvents = []
|
||||
dash.updateSettings({ debug: { logLevel: 0 }, streaming: { abr: { initialBitrate: { video: 150 } }, buffer: { fastSwitchEnabled: true, bufferTimeDefault: 2, bufferTimeAtTopQuality: 2 } } })
|
||||
const events = window.dashjs.MediaPlayer.events
|
||||
dash.on(events.ERROR, event => window.sdkErrors.push({ code: event.error?.code, message: event.error?.message }))
|
||||
for (const name of ['FRAGMENT_LOADING_COMPLETED', 'TRACK_CHANGE_RENDERED', 'QUALITY_CHANGE_RENDERED', 'BUFFER_LEVEL_STATE_CHANGED', 'PLAYBACK_SEEKING', 'PLAYBACK_SEEKED']) {
|
||||
dash.on(events[name], event => window.sdkEvents.push({ name, at: performance.now(), mediaType: event.mediaType, state: event.state, url: event.request?.url, time: video.currentTime }))
|
||||
}
|
||||
for (const name of ['play', 'playing', 'pause', 'seeking', 'seeked', 'waiting']) {
|
||||
video.addEventListener(name, () => window.mediaEvents.push({ name, at: performance.now(), time: video.currentTime, paused: video.paused, readyState: video.readyState, buffered: Array.from({ length: video.buffered.length }, (_, index) => [video.buffered.start(index), video.buffered.end(index)]) }))
|
||||
}
|
||||
dash.initialize(video, '/dash-fixture/master.mpd', false)
|
||||
document.querySelector('#play').onclick = () => {
|
||||
window.nativePlay = 'pending'
|
||||
video.play().then(() => {
|
||||
window.nativePlay = 'fulfilled'
|
||||
}, (error) => {
|
||||
window.nativePlay = `${error.name}: ${error.message}`
|
||||
})
|
||||
}
|
||||
document.querySelector('#pause').onclick = () => video.pause()
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVideo.readyState)).toBeGreaterThanOrEqual(2)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVideo.currentTime)).toBeGreaterThan(0.3)
|
||||
await page.evaluate(() => {
|
||||
const dash = window.nativeDash
|
||||
dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: false } } } })
|
||||
if (dash.setQualityFor)
|
||||
dash.setQualityFor('video', 1, true)
|
||||
else
|
||||
dash.setRepresentationForTypeById('video', '1', true)
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVideo.videoHeight)).toBe(180)
|
||||
// Match the failed integrated run: video data ends at the next seek target.
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVideo.buffered.end(window.nativeVideo.buffered.length - 1))).toBeCloseTo(6, 4)
|
||||
await page.evaluate(() => {
|
||||
const dash = window.nativeDash
|
||||
dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: true } } } })
|
||||
dash.setCurrentTrack(dash.getTracksFor('audio').find(track => track.lang === 'fr'))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.nativeDash.getCurrentTrackFor('audio').lang)).toBe('fr')
|
||||
await page.locator('#pause').click()
|
||||
await page.evaluate(() => {
|
||||
window.nativeVideo.currentTime = 6
|
||||
})
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.nativeVideo.currentTime)).toBeGreaterThan(6.2)
|
||||
expect(await page.evaluate(() => window.nativePlay)).toBe('fulfilled')
|
||||
expect(await page.evaluate(() => window.sdkErrors)).toEqual([])
|
||||
expect(await page.evaluate(() => window.Artplayer.instances.length)).toBe(0)
|
||||
})
|
||||
}
|
||||
|
||||
for (const core of ['published', 'candidate']) {
|
||||
for (const version of ['4.5.2', '5.2.1']) {
|
||||
test(`${core} core / DASH ${version}: explicit update reflects external SDK selection`, async ({ page }, testInfo) => {
|
||||
await openDash(page, version, core, 'candidate', testInfo)
|
||||
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
await page.evaluate(() => {
|
||||
const dash = window.art.dash
|
||||
dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: false } } } })
|
||||
if (dash.setQualityFor)
|
||||
dash.setQualityFor('video', 1)
|
||||
else
|
||||
dash.setRepresentationForTypeById('video', '1')
|
||||
dash.setCurrentTrack(dash.getTracksFor('audio').find(track => track.lang === 'fr'))
|
||||
})
|
||||
await expect.poll(() => page.evaluate(() => window.art.video.videoHeight)).toBe(180)
|
||||
await page.evaluate(() => window.art.plugins.artplayerPluginDashControl.update())
|
||||
await expect(page.locator('.art-control-dash-quality .art-selector-value')).toHaveText('180p')
|
||||
await expect(page.locator('.art-control-dash-audio .art-selector-value')).toHaveText('fr')
|
||||
expect(await page.evaluate(() => ({ quality: window.art.setting.find('dash-quality').tooltip, audio: window.art.setting.find('dash-audio').tooltip }))).toEqual({ quality: '180p', audio: 'fr' })
|
||||
await page.evaluate(() => {
|
||||
window.art.dash.updateSettings({ streaming: { abr: { autoSwitchBitrate: { video: true } } } })
|
||||
window.art.plugins.artplayerPluginDashControl.update()
|
||||
})
|
||||
await expect(page.locator('.art-control-dash-quality .art-selector-value')).toHaveText('Auto')
|
||||
expect(await page.evaluate(() => window.sdkErrors)).toEqual([])
|
||||
})
|
||||
|
||||
test(`${core} core / DASH ${version}: actual source topology replacement and owned cleanup`, async ({ page }, testInfo) => {
|
||||
await openDash(page, version, core, 'candidate', testInfo)
|
||||
await expect(page.locator('.art-control-dash-audio .art-selector-value')).toHaveText('en')
|
||||
await page.locator('#play').click()
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
await page.evaluate(() => {
|
||||
const quality = window.art.controls.cache.get('dash-quality').option
|
||||
const audio = window.art.setting.find('dash-audio')
|
||||
// Registry update mutates the option object; retain the actual old callback.
|
||||
window.retainedQuality = { onSelect: quality.onSelect, item: quality.selector[0] }
|
||||
window.retainedAudio = { onSelect: audio.onSelect, item: audio.selector[1] }
|
||||
})
|
||||
await page.evaluate(() => window.art.switchUrl('/dash-fixture/video-only.mpd'))
|
||||
await expect(page.locator('.art-control-dash-audio')).toHaveCount(0)
|
||||
expect(await page.evaluate(() => Boolean(window.art.setting.find('dash-audio')))).toBe(false)
|
||||
expect(await page.evaluate(() => window.art.dash.getTracksFor('audio'))).toEqual([])
|
||||
await expect.poll(() => page.evaluate(() => window.art.currentTime)).toBeGreaterThan(0.3)
|
||||
await page.evaluate(() => {
|
||||
window.retainedQuality.onSelect(window.retainedQuality.item)
|
||||
window.retainedAudio.onSelect(window.retainedAudio.item)
|
||||
})
|
||||
expect(await page.evaluate(() => window.art.dash.getSettings().streaming.abr.autoSwitchBitrate.video)).toBe(true)
|
||||
await page.evaluate(() => window.art.switchUrl('/dash-fixture/single.mpd'))
|
||||
await expect(page.locator('.art-control-dash-quality .art-selector-item')).toHaveText(['90p', 'Auto'])
|
||||
await expect.poll(() => page.evaluate(() => window.art.video.videoHeight)).toBe(90)
|
||||
await page.evaluate(() => window.art.switchUrl('/dash-fixture/master.mpd'))
|
||||
await expect(page.locator('.art-control-dash-audio .art-selector-value')).toHaveText('en')
|
||||
await expect(page.locator('.art-control-dash-quality .art-selector-item')).toHaveText(['180p', '90p', 'Auto'])
|
||||
expect(await page.evaluate(() => window.sdkDestroyed)).toBe(3)
|
||||
expect(await page.evaluate(() => window.sdkErrors)).toEqual([])
|
||||
await page.evaluate(() => window.art.destroy(false))
|
||||
expect(await page.evaluate(() => window.sdkDestroyed)).toBe(4)
|
||||
await expect(page.locator('.art-control-dash-quality, .art-control-dash-audio')).toHaveCount(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,222 @@
|
||||
{
|
||||
"kind": "generated-dash-media",
|
||||
"version": "ffmpeg version 2025-11-12-git-6cdd2cbe32-full_build-www.gyan.dev Copyright (c) 2000-2025 the FFmpeg developers",
|
||||
"duration": 12,
|
||||
"videoHeights": [
|
||||
90,
|
||||
180
|
||||
],
|
||||
"audio": [
|
||||
{
|
||||
"lang": "en",
|
||||
"frequency": 440
|
||||
},
|
||||
{
|
||||
"lang": "fr",
|
||||
"frequency": 880
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
[
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-n",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"testsrc2=size=160x90:rate=24",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"testsrc2=size=320x180:rate=24",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"sine=frequency=440:sample_rate=48000",
|
||||
"-f",
|
||||
"lavfi",
|
||||
"-i",
|
||||
"sine=frequency=880:sample_rate=48000",
|
||||
"-t",
|
||||
"12",
|
||||
"-map",
|
||||
"0:v",
|
||||
"-map",
|
||||
"1:v",
|
||||
"-map",
|
||||
"2:a",
|
||||
"-map",
|
||||
"3:a",
|
||||
"-c:v",
|
||||
"libx264",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
"-profile:v",
|
||||
"baseline",
|
||||
"-level:v",
|
||||
"3.0",
|
||||
"-b:v:0",
|
||||
"150k",
|
||||
"-b:v:1",
|
||||
"350k",
|
||||
"-g",
|
||||
"48",
|
||||
"-keyint_min",
|
||||
"48",
|
||||
"-sc_threshold",
|
||||
"0",
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-b:a",
|
||||
"48k",
|
||||
"-ac",
|
||||
"2",
|
||||
"-metadata:s:a:0",
|
||||
"language=en",
|
||||
"-metadata:s:a:1",
|
||||
"language=fr",
|
||||
"-f",
|
||||
"dash",
|
||||
"-seg_duration",
|
||||
"2",
|
||||
"-use_template",
|
||||
"1",
|
||||
"-use_timeline",
|
||||
"1",
|
||||
"-adaptation_sets",
|
||||
"id=0,streams=0,1 id=1,streams=2 id=2,streams=3",
|
||||
"-init_seg_name",
|
||||
"init-$RepresentationID$.m4s",
|
||||
"-media_seg_name",
|
||||
"chunk-$RepresentationID$-$Number%05d$.m4s",
|
||||
"master.mpd"
|
||||
]
|
||||
],
|
||||
"files": {
|
||||
"chunk-0-00001.m4s": {
|
||||
"sha256": "0bc9f5c9bfc50526dfdc0241ac5d2da2332097d738450a41887bceb8d0b1f305",
|
||||
"bytes": 32029
|
||||
},
|
||||
"chunk-0-00002.m4s": {
|
||||
"sha256": "a3baefa3f68381a844419beabdee422dc4298c102a7b45a9da16d89b74f461fc",
|
||||
"bytes": 41742
|
||||
},
|
||||
"chunk-0-00003.m4s": {
|
||||
"sha256": "f2540e0a1871967a910e25567aa19ce9aaef002bba84f8cdda8ca9bbc8abd40e",
|
||||
"bytes": 36346
|
||||
},
|
||||
"chunk-0-00004.m4s": {
|
||||
"sha256": "21905f15e90b22cd868d55924d44723b9a02a7b1132384a60b526e49646e9e07",
|
||||
"bytes": 41226
|
||||
},
|
||||
"chunk-0-00005.m4s": {
|
||||
"sha256": "9d0cb0486da40f3a4e6819a5964d4db945305d328cce6ce2c21a60a57c413f42",
|
||||
"bytes": 35119
|
||||
},
|
||||
"chunk-0-00006.m4s": {
|
||||
"sha256": "f52fd4ecf9d0276261b183c6e44096ad2f28267a5f2a0a15ac65cec8705b605f",
|
||||
"bytes": 38357
|
||||
},
|
||||
"chunk-1-00001.m4s": {
|
||||
"sha256": "389d6f02741679ed7ef43f529bbec746b5e1b62a97519ca80be16f114e76845c",
|
||||
"bytes": 72180
|
||||
},
|
||||
"chunk-1-00002.m4s": {
|
||||
"sha256": "5cc737843b84a064acb65d985b2ea7ff151c26937f5bf48cacdf0f39c9e60122",
|
||||
"bytes": 94545
|
||||
},
|
||||
"chunk-1-00003.m4s": {
|
||||
"sha256": "2a1b56d88858f3d9b6070ab8645d878b12f8baa29c567b038d190f89c2f863e6",
|
||||
"bytes": 84303
|
||||
},
|
||||
"chunk-1-00004.m4s": {
|
||||
"sha256": "be7198ae2b2d04e27748fcbe8669734a7ead3a51c25fb7f1c9d981740bf5f92d",
|
||||
"bytes": 95672
|
||||
},
|
||||
"chunk-1-00005.m4s": {
|
||||
"sha256": "502e3dd1ce6ca85c442ba849e605a1b0cb1ca23472af6fe6e0dacaea8a38d5dc",
|
||||
"bytes": 82244
|
||||
},
|
||||
"chunk-1-00006.m4s": {
|
||||
"sha256": "34d6e27576dc3c834d0aff9e5925581a899c354c4fdd34fc639cc63f9807c62f",
|
||||
"bytes": 90139
|
||||
},
|
||||
"chunk-2-00001.m4s": {
|
||||
"sha256": "23eaaffa433ed1808f57b95e14b85fba15b2d7e8eea6c51fd02ce409dbccf7de",
|
||||
"bytes": 12437
|
||||
},
|
||||
"chunk-2-00002.m4s": {
|
||||
"sha256": "f40e15cb8d377dcd4eeac508493694e4a131fd4a0d970ff2636b1b40fc728f0a",
|
||||
"bytes": 12641
|
||||
},
|
||||
"chunk-2-00003.m4s": {
|
||||
"sha256": "e1a1f49359d23f460f9b0a32df5f5da7ba6d234fe59cc4a9c0a5f7e201fed643",
|
||||
"bytes": 12663
|
||||
},
|
||||
"chunk-2-00004.m4s": {
|
||||
"sha256": "a5b0625a33a62cf2b73414c92b7c9f6ac945650d3ff6dadaff26705d10920bf6",
|
||||
"bytes": 12657
|
||||
},
|
||||
"chunk-2-00005.m4s": {
|
||||
"sha256": "ed446e2eb99474765e9b0eb40728bd5d79eba79e74b5fa688a0dbddfb3f4b82a",
|
||||
"bytes": 12682
|
||||
},
|
||||
"chunk-2-00006.m4s": {
|
||||
"sha256": "6b969d834a87836f84eac8769fba2962c8303aacbb832f4710e642a371c6e38e",
|
||||
"bytes": 13008
|
||||
},
|
||||
"chunk-3-00001.m4s": {
|
||||
"sha256": "9171ffc0421c4dc0f874eb8801c2df0b4bf312981a8841455b13081cca1b0feb",
|
||||
"bytes": 12453
|
||||
},
|
||||
"chunk-3-00002.m4s": {
|
||||
"sha256": "da7a827ad08e9b48647f2bc6c90d5db1b53ef15965575d38b7e9facbddd37fc1",
|
||||
"bytes": 12644
|
||||
},
|
||||
"chunk-3-00003.m4s": {
|
||||
"sha256": "3d5f9fd99a21ce3d59f11416936d79252a12bc95560988f1abe4ccf802733ebc",
|
||||
"bytes": 12643
|
||||
},
|
||||
"chunk-3-00004.m4s": {
|
||||
"sha256": "eeca9b9a3f91c8491ad663cb5d686b168c530dc771d46c35ff5919b3a8b4ee37",
|
||||
"bytes": 12678
|
||||
},
|
||||
"chunk-3-00005.m4s": {
|
||||
"sha256": "747526362131944d696b09059ba0a19c106e363a30fa07cf7ec86081c4e277e8",
|
||||
"bytes": 12582
|
||||
},
|
||||
"chunk-3-00006.m4s": {
|
||||
"sha256": "5234fd26fd201536c839a1740d3bd57a5fe8c86e3e180f7f1e4c70cc41f30072",
|
||||
"bytes": 13077
|
||||
},
|
||||
"init-0.m4s": {
|
||||
"sha256": "d0a16341c48647e27452db83de88f88209dbec7e539e159e5057469d28980881",
|
||||
"bytes": 827
|
||||
},
|
||||
"init-1.m4s": {
|
||||
"sha256": "3e814e958e68e98f947883a2b7511a36fb06db127ea074ccd569af0200839948",
|
||||
"bytes": 828
|
||||
},
|
||||
"init-2.m4s": {
|
||||
"sha256": "866460ae9e8465b1473c3dbbc63f38416815d75d906790109575cf1de8332423",
|
||||
"bytes": 764
|
||||
},
|
||||
"init-3.m4s": {
|
||||
"sha256": "866460ae9e8465b1473c3dbbc63f38416815d75d906790109575cf1de8332423",
|
||||
"bytes": 764
|
||||
},
|
||||
"master.mpd": {
|
||||
"sha256": "6a4cb0a956861dee9aff1c592525c41fb4cf660be86b4c3892a91a46aeec53ef",
|
||||
"bytes": 2986
|
||||
},
|
||||
"single.mpd": {
|
||||
"sha256": "bdbd8a027281cefec1d4021d7e5a2731875188eb331612bc35eef75f265a700b",
|
||||
"bytes": 1227
|
||||
},
|
||||
"video-only.mpd": {
|
||||
"sha256": "b7f8d37c023810ecc38a2564d4644eda4b5a0b0dd2d10dfd39f70a4b49be0566",
|
||||
"bytes": 1622
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
||||
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
||||
type="static"
|
||||
mediaPresentationDuration="PT12.0S"
|
||||
maxSegmentDuration="PT2.0S"
|
||||
minBufferTime="PT4.0S">
|
||||
<ProgramInformation>
|
||||
</ProgramInformation>
|
||||
<ServiceDescription id="0">
|
||||
</ServiceDescription>
|
||||
<Period id="0" start="PT0.0S">
|
||||
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="24/1" maxWidth="320" maxHeight="180" par="16:9">
|
||||
<Representation id="0" mimeType="video/mp4" codecs="avc1.42c01e" bandwidth="150000" width="160" height="90" sar="1:1">
|
||||
<SegmentTemplate timescale="12288" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="24576" r="5" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
<Representation id="1" mimeType="video/mp4" codecs="avc1.42c01e" bandwidth="350000" width="320" height="180" sar="1:1">
|
||||
<SegmentTemplate timescale="12288" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="24576" r="5" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" lang="en">
|
||||
<Representation id="2" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="48000" audioSamplingRate="48000">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
|
||||
<SegmentTemplate timescale="48000" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="95232" />
|
||||
<S d="96256" r="3" />
|
||||
<S d="95744" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="2" contentType="audio" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" lang="fr">
|
||||
<Representation id="3" mimeType="audio/mp4" codecs="mp4a.40.2" bandwidth="48000" audioSamplingRate="48000">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2" />
|
||||
<SegmentTemplate timescale="48000" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="95232" />
|
||||
<S d="96256" r="3" />
|
||||
<S d="95744" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
</Period>
|
||||
</MPD>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
||||
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
||||
type="static"
|
||||
mediaPresentationDuration="PT12.0S"
|
||||
maxSegmentDuration="PT2.0S"
|
||||
minBufferTime="PT4.0S">
|
||||
<ProgramInformation>
|
||||
</ProgramInformation>
|
||||
<ServiceDescription id="0">
|
||||
</ServiceDescription>
|
||||
<Period id="0" start="PT0.0S">
|
||||
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="24/1" maxWidth="320" maxHeight="180" par="16:9">
|
||||
<Representation id="0" mimeType="video/mp4" codecs="avc1.42c01e" bandwidth="150000" width="160" height="90" sar="1:1">
|
||||
<SegmentTemplate timescale="12288" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="24576" r="5" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
|
||||
</AdaptationSet>
|
||||
|
||||
|
||||
</Period>
|
||||
</MPD>
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="urn:mpeg:dash:schema:mpd:2011"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
|
||||
profiles="urn:mpeg:dash:profile:isoff-live:2011"
|
||||
type="static"
|
||||
mediaPresentationDuration="PT12.0S"
|
||||
maxSegmentDuration="PT2.0S"
|
||||
minBufferTime="PT4.0S">
|
||||
<ProgramInformation>
|
||||
</ProgramInformation>
|
||||
<ServiceDescription id="0">
|
||||
</ServiceDescription>
|
||||
<Period id="0" start="PT0.0S">
|
||||
<AdaptationSet id="0" contentType="video" startWithSAP="1" segmentAlignment="true" bitstreamSwitching="true" frameRate="24/1" maxWidth="320" maxHeight="180" par="16:9">
|
||||
<Representation id="0" mimeType="video/mp4" codecs="avc1.42c01e" bandwidth="150000" width="160" height="90" sar="1:1">
|
||||
<SegmentTemplate timescale="12288" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="24576" r="5" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
<Representation id="1" mimeType="video/mp4" codecs="avc1.42c01e" bandwidth="350000" width="320" height="180" sar="1:1">
|
||||
<SegmentTemplate timescale="12288" initialization="init-$RepresentationID$.m4s" media="chunk-$RepresentationID$-$Number%05d$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="0" d="24576" r="5" />
|
||||
</SegmentTimeline>
|
||||
</SegmentTemplate>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
||||
|
||||
</Period>
|
||||
</MPD>
|
||||
Reference in new issue
Block a user