From 941b325b90d24af0849c5c8e51ebf151fb064fc0 Mon Sep 17 00:00:00 2001 From: Lloyd Wallis Date: Sat, 6 Aug 2016 21:06:21 +0100 Subject: [PATCH] Add a working test for a basic ondemand SegmentTemplate. Getting a Representation is far harder than expected - DashManifestModel methods starting with getAdaptation... doesn't mean it'll get an adaptation, and it generally expects things to have happened in e.g. DashAdapter or DashHandler that augment the default objects before it works... Also always calculates full list of segments for an ondemand stream. --- .jshintrc | 1 + externals/xml2json.js | 7 +- package.json | 3 +- src/core/Debug.js | 14 + src/dash/parser/DashParser.js | 10 +- src/dash/utils/SegmentsUtils.js | 2 +- test/dash.SegmentsGetterSpec.js | 56 ++- test/helpers/MPDfiles.js | 774 ++++++++------------------------ test/helpers/ObjectsHelper.js | 7 + 9 files changed, 282 insertions(+), 592 deletions(-) mode change 100644 => 100755 package.json diff --git a/.jshintrc b/.jshintrc index d139ba7120..02b7556ee5 100644 --- a/.jshintrc +++ b/.jshintrc @@ -12,6 +12,7 @@ "devel" : true, "browser": true, "loopfunc" : true, + "multistr": true, "predef" : [ "it", "describe", diff --git a/externals/xml2json.js b/externals/xml2json.js index 101e29fccf..7470bb3e29 100644 --- a/externals/xml2json.js +++ b/externals/xml2json.js @@ -70,10 +70,11 @@ function X2JS(matchers, attrPrefix, ignoreRoot) { i, len; - // get the first node that isn't a comment + // get the first node that we understand for(i = 0, len = node.childNodes.length; i < len; i += 1) { - if (node.childNodes[i].nodeType !== DOMNodeTypes.COMMENT_NODE) { - child = node.childNodes[i]; + let cNode = node.childNodes[i]; + if (cNode.nodeType == DOMNodeTypes.ELEMENT_NODE || cNode.nodeType == DOMNodeTypes.TEXT_NODE || cNode.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) { + child = cNode; break; } } diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 5883162401..8b803e7a7c --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "mocha": "^2.3.4", "sinon": "^1.17.2", "time-grunt": "^1.2.0", - "uglify-js": "^2.4.21" + "uglify-js": "^2.4.21", + "xmldom": "^0.1.22" }, "dependencies": { "codem-isoboxer": "0.2.2", diff --git a/src/core/Debug.js b/src/core/Debug.js index 170c4b569d..3f19c5d3b0 100644 --- a/src/core/Debug.js +++ b/src/core/Debug.js @@ -111,11 +111,25 @@ function Debug() { eventBus.trigger(Events.LOG, {message: message}); } + /** + * Returns window.performance.now(), if available + * Otherwise falls back to Date() + * mocha doesn't have window.performance and it's possible some UAs don't either + */ + function timer() { + if (typeof performance !== "undefined") { + return performance.now(); + } else { + return (new Date()).getTime(); + } + } + instance = { log: log, setLogTimestampVisible: setLogTimestampVisible, setLogToBrowserConsole: setLogToBrowserConsole, getLogToBrowserConsole: getLogToBrowserConsole, + timer: timer }; setup(); diff --git a/src/dash/parser/DashParser.js b/src/dash/parser/DashParser.js index 1b972ac4da..c7ea0392e0 100644 --- a/src/dash/parser/DashParser.js +++ b/src/dash/parser/DashParser.js @@ -42,7 +42,9 @@ import SegmentValuesMap from './maps/SegmentValuesMap'; function DashParser(/*config*/) { const context = this.context; - const log = Debug(context).getInstance().log; + const debug = Debug(context).getInstance(); + const log = debug.log; + const timer = debug.timer; const errorHandler = ErrorHandler(context).getInstance(); let instance, @@ -69,7 +71,7 @@ function DashParser(/*config*/) { var manifest; try { - const startTime = window.performance.now(); + const startTime = timer(); manifest = converter.xml_str2json(data); @@ -77,11 +79,11 @@ function DashParser(/*config*/) { throw new Error('parser error'); } - const jsonTime = window.performance.now(); + const jsonTime = timer(); objectIron.run(manifest); - const ironedTime = window.performance.now(); + const ironedTime = timer(); xlinkController.setMatchers(matchers); xlinkController.setIron(objectIron); diff --git a/src/dash/utils/SegmentsUtils.js b/src/dash/utils/SegmentsUtils.js index f7b375f1fd..c0f2708be2 100644 --- a/src/dash/utils/SegmentsUtils.js +++ b/src/dash/utils/SegmentsUtils.js @@ -255,7 +255,7 @@ export function decideSegmentListRangeForTemplate(timelineConverter, isDynamic, }; var currentSegmentList = representation.segments; var availabilityLowerLimit = 2 * duration; - var availabilityUpperLimit = givenAvailabilityUpperLimit || Math.max(2 * minBufferTime, 10 * duration); + var availabilityUpperLimit = givenAvailabilityUpperLimit || Number.POSITIVE_INFINITY; var originAvailabilityTime = NaN; var originSegment = null; diff --git a/test/dash.SegmentsGetterSpec.js b/test/dash.SegmentsGetterSpec.js index 6d6bc52d0a..bfb37bf6f7 100755 --- a/test/dash.SegmentsGetterSpec.js +++ b/test/dash.SegmentsGetterSpec.js @@ -1,19 +1,54 @@ import ObjectsHelper from './helpers/ObjectsHelper'; import VoHelper from './helpers/VOHelper'; +import MPDfiles from './helpers/MPDfiles'; + import SegmentsGetter from '../src/dash/utils/SegmentsGetter'; +import DashParser from '../src/dash/parser/DashParser'; +import DashManifestModel from '../src/dash/models/DashManifestModel'; +import TimelineConverter from '../src/dash/utils/TimelineConverter'; const expect = require('chai').expect; +const DOMParser = require('xmldom').DOMParser; describe('SegmentsGetter', function () { const objectsHelper = new ObjectsHelper(); const voHelper = new VoHelper(); + const createSegmentsGetter = (isDynamic) => { const context = {}; - const config = {}; + const config = { + timelineConverter: createTimelineConverter() + }; return SegmentsGetter(context).create(config, !!isDynamic); }; + const createDashParser = () => { + const context = {}; + + return DashParser(context).create(); + }; + + const createDashManifestModel = () => { + return DashManifestModel({}).getInstance(); + }; + + const createTimelineConverter = () => { + return TimelineConverter({}).getInstance(); + }; + + const parseManifest = xmlStr => { + const manifest = createDashParser().parse(xmlStr, objectsHelper.getDummyXlinkController()); + manifest.loadedTime = {getTime: () => 0}; + return manifest; + }; + + beforeEach(() => { + global.window = { + DOMParser: DOMParser + }; + }); + it('should not regenerate segments for a static MPD if they are already there', () => { const segmentsGetter = createSegmentsGetter(false); const representation = voHelper.createRepresentation('audio'); @@ -29,4 +64,23 @@ describe('SegmentsGetter', function () { expect(segments[1]).to.deep.equal(voHelper.createSegment(1)); expect(segments.length).to.equal(2); }); + + it('should calculate the correct number of segments for an ondemand SegmentTemplate manifest', (done) => { + const model = createDashManifestModel(); + const timelineConverter = createTimelineConverter(); + const manifest = parseManifest(MPDfiles.bbcrdtestcard); + const mpd = model.getMpd(manifest); + const period = model.getRegularPeriods(manifest, mpd)[0]; + const adaptationIndex = model.getIndexForAdaptation(model.getAdaptationForType(manifest, 0, 'video'), manifest, period.index); + const adaptationSet = model.getAdaptationsForPeriod(manifest, period)[adaptationIndex]; + const representations = model.getRepresentationsForAdaptation(manifest, adaptationSet); + const representation = representations[representations.length - 1]; + + representation.segmentAvailabilityRange = timelineConverter.calcSegmentAvailabilityRange(representation, model.getIsDynamic(manifest)); + + createSegmentsGetter().getSegments(representation, 0, 0, (representation, segments) => { + expect(segments.length).to.equal(938); + done(); + }); + }); }); diff --git a/test/helpers/MPDfiles.js b/test/helpers/MPDfiles.js index 9dfd9e61aa..45d4a03318 100644 --- a/test/helpers/MPDfiles.js +++ b/test/helpers/MPDfiles.js @@ -14,588 +14,198 @@ var strMpd = { - //http://dash.edgesuite.net/dash264/TestCases/1b/thomson-networks/manifest.mpd - MPD1 : '\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - ', + //http://dash.edgesuite.net/dash264/TestCases/1b/thomson-networks/manifest.mpd + test1c: '\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ', - //http://www.digitalprimates.net/dash/streams/gpac/mp4-main-multi-mpd-AV-NBS.mpd - MPD2 : '\ - \ - mp4-main-multi-mpd-AV-NBS.mpd generated by GPAC\ - TelecomParisTech(c)2012\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - ', - - //http://www.digitalprimates.net/dash/streams/mp4-live-template/mp4-live-mpd-AV-BS.mpd - MPD3 : '\ - \ - mp4-live-mpd-AV-BS.mpd generated by GPAC\ - TelecomParisTech(c)2012\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - ', - //http://demo.unified-streaming.com/video/ateam/ateam.ism/ateam.mpd - MPD4 : '\ - http://demo.unified-streaming.com/video/ateam/ateam.ism/\ - \ - dash/\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - ' + //http://demo.unified-streaming.com/video/ateam/ateam.ism/ateam.mpd + uspateam: '\ + http://demo.unified-streaming.com/video/ateam/ateam.ism/\ + \ + dash/\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + ', + bbcrdtestcard: '\ + \ + \ + \ + \ + \ + \ + \ + Adaptive Bitrate Test Stream from BBC Research and Development\ + BBC Research and Development\ + British Broadcasting Corporation 2014\ + \ + http://rdmedia.bbc.co.uk/dash/ondemand/testcard/1/\ + \ + \ + \ + \ + avc3-events/\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + audio/\ + \ + \ + \ + \ + \ + \ + \ + audio/\ + \ + \ + \ + \ + \ + \ + audio/\ + \ + \ + \ + \ + \ + \ + \ + ' }; -var emptyMpdData = { - MPD1 : '\ - \ - ', -}; +export default strMpd; diff --git a/test/helpers/ObjectsHelper.js b/test/helpers/ObjectsHelper.js index 999cb7b6bb..35dc7f098d 100644 --- a/test/helpers/ObjectsHelper.js +++ b/test/helpers/ObjectsHelper.js @@ -44,6 +44,13 @@ class ObjectsHelper { contains: () => {} }; } + + getDummyXlinkController() { + return { + setMatchers: () => {}, + setIron: () => {} + }; + } } export default ObjectsHelper;